Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

migrate to twisted #2

  • Loading branch information...
commit 6939bdf3ac5267059e14e3c575618475a3354ba2 1 parent c797774
@bitcraft authored
Showing with 16,290 additions and 0 deletions.
  1. +7 −0 cleanup.sh
  2. +31 −0 construct/README.rst
  3. +110 −0 construct/__init__.py
  4. +468 −0 construct/adapters.py
  5. +1,322 −0 construct/core.py
  6. +160 −0 construct/debug.py
  7. 0  construct/formats/__init__.py
  8. +3 −0  construct/formats/data/__init__.py
  9. +55 −0 construct/formats/data/cap.py
  10. 0  construct/formats/executable/__init__.py
  11. +156 −0 construct/formats/executable/elf32.py
  12. +419 −0 construct/formats/executable/pe32.py
  13. +4 −0 construct/formats/filesystem/__init__.py
  14. +157 −0 construct/formats/filesystem/ext2.py
  15. +76 −0 construct/formats/filesystem/mbr.py
  16. +4 −0 construct/formats/graphics/__init__.py
  17. +113 −0 construct/formats/graphics/bmp.py
  18. +198 −0 construct/formats/graphics/emf.py
  19. +354 −0 construct/formats/graphics/png.py
  20. +129 −0 construct/formats/graphics/wmf.py
  21. +10 −0 construct/lib/__init__.py
  22. +59 −0 construct/lib/binary.py
  23. +77 −0 construct/lib/bitstream.py
  24. +165 −0 construct/lib/container.py
  25. +35 −0 construct/lib/hex.py
  26. +634 −0 construct/macros.py
  27. +4 −0 construct/protocols/__init__.py
  28. +4 −0 construct/protocols/application/__init__.py
  29. +147 −0 construct/protocols/application/dns.py
  30. +154 −0 construct/protocols/application/http.py
  31. +305 −0 construct/protocols/application/telnet.py
  32. +135 −0 construct/protocols/ipstack.py
  33. +4 −0 construct/protocols/layer2/__init__.py
  34. +92 −0 construct/protocols/layer2/arp.py
  35. +36 −0 construct/protocols/layer2/ethernet.py
  36. +21 −0 construct/protocols/layer2/mtp2.py
  37. +4 −0 construct/protocols/layer3/__init__.py
  38. +210 −0 construct/protocols/layer3/dhcpv4.py
  39. +111 −0 construct/protocols/layer3/dhcpv6.py
  40. +96 −0 construct/protocols/layer3/icmpv4.py
  41. +72 −0 construct/protocols/layer3/ipv4.py
  42. +44 −0 construct/protocols/layer3/ipv6.py
  43. +12 −0 construct/protocols/layer3/mtp3.py
  44. +4 −0 construct/protocols/layer4/__init__.py
  45. +15 −0 construct/protocols/layer4/isup.py
  46. +57 −0 construct/protocols/layer4/tcp.py
  47. +24 −0 construct/protocols/layer4/udp.py
  48. 0  construct/tests/__init__.py
  49. +95 −0 construct/tests/csvtest.py
  50. +20 −0 construct/tests/debug.py
  51. 0  construct/tests/lib/__init__.py
  52. +89 −0 construct/tests/lib/test_container.py
  53. +12 −0 construct/tests/t1.py
  54. +60 −0 construct/tests/test_adaptors.py
  55. +137 −0 construct/tests/test_ast.py
  56. +31 −0 construct/tests/test_bit.py
  57. +99 −0 construct/tests/test_core.py
  58. +34 −0 construct/tests/test_lib.py
  59. +29 −0 construct/tests/test_mapping.py
  60. +97 −0 construct/tests/test_repeaters.py
  61. +83 −0 construct/tests/test_strings.py
  62. +15 −0 construct/tests/test_text.py
  63. +30 −0 construct/tests/testall.py
  64. +77 −0 construct/tests/text.py
  65. +340 −0 construct/tests/unit.py
  66. +4 −0 construct/text/__init__.py
  67. +18 −0 construct/text/ast.py
  68. +331 −0 construct/text/common.py
  69. +306 −0 construct/text/test.py
  70. 0  lib2d/beta/__init__.py
  71. +570 −0 lib2d/beta/factory.py
  72. +725 −0 lib2d/beta/packets.py
  73. +1,396 −0 lib2d/beta/protocol.py
  74. +223 −0 lib2d/beta/recipes.py
  75. +69 −0 lib2d/beta/structures.py
  76. +1 −0  lib2d/client/__init__.py
  77. +57 −0 lib2d/client/buttons.py
  78. +31 −0 lib2d/client/game.py
  79. +88 −0 lib2d/client/gamestate.py
  80. +91 −0 lib2d/client/gfx.py
  81. +384 −0 lib2d/client/gui.py
  82. +289 −0 lib2d/client/menu.py
  83. +171 −0 lib2d/client/playerinput.py
  84. +287 −0 lib2d/client/statedriver.py
  85. +120 −0 lib2d/client/subpixelsurface.py
  86. +455 −0 lib2d/client/tilemap.py
  87. +323 −0 lib2d/gameobject.py
  88. +1 −0  lib2d/pytmx/__init__.py
  89. +89 −0 lib2d/pytmx/maputils.py
  90. +967 −0 lib2d/pytmx/tmxloader.py
  91. +1 −0  lib2d/server/__init__.py
  92. +820 −0 lib2d/server/area.py
  93. +260 −0 lib2d/server/bbox.py
  94. +119 −0 lib2d/server/factory.py
  95. +121 −0 lib2d/server/packets.py
  96. +126 −0 lib2d/server/protocol.py
  97. +301 −0 lib2d/server/quadtree.py
  98. +1 −0  test.sh
View
7 cleanup.sh
@@ -0,0 +1,7 @@
+find . -name \.DS_Store -type f -delete
+find . -name \*~ -type f -delete
+find . -name \*pyc -type f -delete
+find . -name \*.swp -type f -delete
+find . -type d -print0 | xargs -0 chmod 755
+find . -type f -print0 | xargs -0 chmod 644
+find . -type f -print0 | xargs -0 touch -ma
View
31 construct/README.rst
@@ -0,0 +1,31 @@
+=========
+Construct
+=========
+
+Construct is a powerful declarative parser for binary data.
+
+It is based on the concept of defining data structures in a declarative
+manner, rather than procedural code: Simple constructs can be combined
+hierarchically to form increasingly complex data structures. It's the first
+library that makes parsing fun, instead of the usual headache it is today.
+
+Construct features bit and byte granularity, symmetrical operation (parsing
+and building), component-oriented declarative design, easy debugging and
+testing, an easy-to-extend subclass system, and lots of primitive
+constructs to make your work easier:
+
+ * Fields
+ * Structs
+ * Unions
+ * Repeaters
+ * Meta constructs
+ * Switches
+ * On-demand parsing
+ * Pointers
+ * And more!
+
+Requirements
+============
+
+Construct should run on any Python 2.5+ implementation. It has no external
+dependencies.
View
110 construct/__init__.py
@@ -0,0 +1,110 @@
+"""
+ #### ####
+ ## #### ## ## #### ###### ##### ## ## #### ###### ## ##
+ ## ## ## ### ## ## ## ## ## ## ## ## ## #### ##
+ ## ## ## ###### ### ## ##### ## ## ## ## ##
+ ## ## ## ## ### ## ## ## ## ## ## ## ## ##
+ #### #### ## ## #### ## ## ## ##### #### ## ######
+
+ Parsing made even more fun (and faster too)
+
+Homepage:
+ http://construct.wikispaces.com (including online tutorial)
+
+Typical usage:
+ >>> from construct import *
+
+Hands-on example:
+ >>> from construct import *
+ >>> s = Struct("foo",
+ ... UBInt8("a"),
+ ... UBInt16("b"),
+ ... )
+ >>> s.parse("\\x01\\x02\\x03")
+ Container(a = 1, b = 515)
+ >>> print s.parse("\\x01\\x02\\x03")
+ Container:
+ a = 1
+ b = 515
+ >>> s.build(Container(a = 1, b = 0x0203))
+ "\\x01\\x02\\x03"
+"""
+
+from construct.core import *
+from construct.adapters import *
+from construct.macros import *
+from debug import Probe, Debugger
+
+
+#===============================================================================
+# Metadata
+#===============================================================================
+__author__ = "tomer filiba (tomerfiliba [at] gmail.com)"
+__maintainer__ = "Corbin Simpson <MostAwesomeDude@gmail.com>"
+__version__ = "2.06"
+
+#===============================================================================
+# Shorthand expressions
+#===============================================================================
+Bits = BitField
+Byte = UBInt8
+Bytes = Field
+Const = ConstAdapter
+Tunnel = TunnelAdapter
+Embed = Embedded
+
+#===============================================================================
+# Deprecated names
+# Next scheduled name cleanout: 2.1
+#===============================================================================
+import functools, warnings
+
+def deprecated(f):
+ @functools.wraps(f)
+ def wrapper(*args, **kwargs):
+ warnings.warn(
+ "This name is deprecated, use %s instead" % f.__name__,
+ DeprecationWarning, stacklevel=2)
+ return f(*args, **kwargs)
+ return wrapper
+
+MetaBytes = deprecated(MetaField)
+GreedyRepeater = deprecated(GreedyRange)
+OptionalGreedyRepeater = deprecated(OptionalGreedyRange)
+Repeater = deprecated(Range)
+StrictRepeater = deprecated(Array)
+MetaRepeater = deprecated(Array)
+OneOfValidator = deprecated(OneOf)
+NoneOfValidator = deprecated(NoneOf)
+
+#===============================================================================
+# exposed names
+#===============================================================================
+__all__ = [
+ 'AdaptationError', 'Adapter', 'Alias', 'Aligned', 'AlignedStruct',
+ 'Anchor', 'Array', 'ArrayError', 'BFloat32', 'BFloat64', 'Bit', 'BitField',
+ 'BitIntegerAdapter', 'BitIntegerError', 'BitStruct', 'Bits', 'Bitwise',
+ 'Buffered', 'Byte', 'Bytes', 'CString', 'CStringAdapter', 'Const',
+ 'ConstAdapter', 'ConstError', 'Construct', 'ConstructError', 'Container',
+ 'Debugger', 'Embed', 'Embedded', 'EmbeddedBitStruct', 'Enum', 'ExprAdapter',
+ 'Field', 'FieldError', 'Flag', 'FlagsAdapter', 'FlagsContainer',
+ 'FlagsEnum', 'FormatField', 'GreedyRange', 'GreedyRepeater',
+ 'HexDumpAdapter', 'If', 'IfThenElse', 'IndexingAdapter', 'LFloat32',
+ 'LFloat64', 'LazyBound', 'LengthValueAdapter', 'ListContainer',
+ 'MappingAdapter', 'MappingError', 'MetaArray', 'MetaBytes', 'MetaField',
+ 'MetaRepeater', 'NFloat32', 'NFloat64', 'Nibble', 'NoneOf',
+ 'NoneOfValidator', 'Octet', 'OnDemand', 'OnDemandPointer', 'OneOf',
+ 'OneOfValidator', 'OpenRange', 'Optional', 'OptionalGreedyRange',
+ 'OptionalGreedyRepeater', 'PaddedStringAdapter', 'Padding',
+ 'PaddingAdapter', 'PaddingError', 'PascalString', 'Pass', 'Peek',
+ 'Pointer', 'PrefixedArray', 'Probe', 'Range', 'RangeError', 'Reconfig',
+ 'Rename', 'RepeatUntil', 'Repeater', 'Restream', 'SBInt16', 'SBInt32',
+ 'SBInt64', 'SBInt8', 'SLInt16', 'SLInt32', 'SLInt64', 'SLInt8', 'SNInt16',
+ 'SNInt32', 'SNInt64', 'SNInt8', 'Select', 'SelectError', 'Sequence',
+ 'SizeofError', 'SlicingAdapter', 'StaticField', 'StrictRepeater', 'String',
+ 'StringAdapter', 'Struct', 'Subconstruct', 'Switch', 'SwitchError',
+ 'SymmetricMapping', 'Terminator', 'TerminatorError', 'Tunnel',
+ 'TunnelAdapter', 'UBInt16', 'UBInt32', 'UBInt64', 'UBInt8', 'ULInt16',
+ 'ULInt32', 'ULInt64', 'ULInt8', 'UNInt16', 'UNInt32', 'UNInt64', 'UNInt8',
+ 'Union', 'ValidationError', 'Validator', 'Value', "Magic",
+]
View
468 construct/adapters.py
@@ -0,0 +1,468 @@
+from core import Adapter, AdaptationError, Pass
+from lib import int_to_bin, bin_to_int, swap_bytes, StringIO
+from lib import FlagsContainer, HexString
+
+
+#===============================================================================
+# exceptions
+#===============================================================================
+class BitIntegerError(AdaptationError):
+ __slots__ = []
+class MappingError(AdaptationError):
+ __slots__ = []
+class ConstError(AdaptationError):
+ __slots__ = []
+class ValidationError(AdaptationError):
+ __slots__ = []
+class PaddingError(AdaptationError):
+ __slots__ = []
+
+#===============================================================================
+# adapters
+#===============================================================================
+class BitIntegerAdapter(Adapter):
+ """
+ Adapter for bit-integers (converts bitstrings to integers, and vice versa).
+ See BitField.
+
+ Parameters:
+ * subcon - the subcon to adapt
+ * width - the size of the subcon, in bits
+ * swapped - whether to swap byte order (little endian/big endian).
+ default is False (big endian)
+ * signed - whether the value is signed (two's complement). the default
+ is False (unsigned)
+ * bytesize - number of bits per byte, used for byte-swapping (if swapped).
+ default is 8.
+ """
+ __slots__ = ["width", "swapped", "signed", "bytesize"]
+ def __init__(self, subcon, width, swapped = False, signed = False,
+ bytesize = 8):
+ Adapter.__init__(self, subcon)
+ self.width = width
+ self.swapped = swapped
+ self.signed = signed
+ self.bytesize = bytesize
+ def _encode(self, obj, context):
+ if obj < 0 and not self.signed:
+ raise BitIntegerError("object is negative, but field is not signed",
+ obj)
+ obj2 = int_to_bin(obj, width = self.width)
+ if self.swapped:
+ obj2 = swap_bytes(obj2, bytesize = self.bytesize)
+ return obj2
+ def _decode(self, obj, context):
+ if self.swapped:
+ obj = swap_bytes(obj, bytesize = self.bytesize)
+ return bin_to_int(obj, signed = self.signed)
+
+class MappingAdapter(Adapter):
+ """
+ Adapter that maps objects to other objects.
+ See SymmetricMapping and Enum.
+
+ Parameters:
+ * subcon - the subcon to map
+ * decoding - the decoding (parsing) mapping (a dict)
+ * encoding - the encoding (building) mapping (a dict)
+ * decdefault - the default return value when the object is not found
+ in the decoding mapping. if no object is given, an exception is raised.
+ if `Pass` is used, the unmapped object will be passed as-is
+ * encdefault - the default return value when the object is not found
+ in the encoding mapping. if no object is given, an exception is raised.
+ if `Pass` is used, the unmapped object will be passed as-is
+ """
+ __slots__ = ["encoding", "decoding", "encdefault", "decdefault"]
+ def __init__(self, subcon, decoding, encoding,
+ decdefault = NotImplemented, encdefault = NotImplemented):
+ Adapter.__init__(self, subcon)
+ self.decoding = decoding
+ self.encoding = encoding
+ self.decdefault = decdefault
+ self.encdefault = encdefault
+ def _encode(self, obj, context):
+ try:
+ return self.encoding[obj]
+ except (KeyError, TypeError):
+ if self.encdefault is NotImplemented:
+ raise MappingError("no encoding mapping for %r" % (obj,))
+ if self.encdefault is Pass:
+ return obj
+ return self.encdefault
+ def _decode(self, obj, context):
+ try:
+ return self.decoding[obj]
+ except (KeyError, TypeError):
+ if self.decdefault is NotImplemented:
+ raise MappingError("no decoding mapping for %r" % (obj,))
+ if self.decdefault is Pass:
+ return obj
+ return self.decdefault
+
+class FlagsAdapter(Adapter):
+ """
+ Adapter for flag fields. Each flag is extracted from the number, resulting
+ in a FlagsContainer object. Not intended for direct usage.
+ See FlagsEnum.
+
+ Parameters
+ * subcon - the subcon to extract
+ * flags - a dictionary mapping flag-names to their value
+ """
+ __slots__ = ["flags"]
+ def __init__(self, subcon, flags):
+ Adapter.__init__(self, subcon)
+ self.flags = flags
+ def _encode(self, obj, context):
+ flags = 0
+ for name, value in self.flags.iteritems():
+ if getattr(obj, name, False):
+ flags |= value
+ return flags
+ def _decode(self, obj, context):
+ obj2 = FlagsContainer()
+ for name, value in self.flags.iteritems():
+ setattr(obj2, name, bool(obj & value))
+ return obj2
+
+class StringAdapter(Adapter):
+ """
+ Adapter for strings. Converts a sequence of characters into a python
+ string, and optionally handles character encoding.
+ See String.
+
+ Parameters:
+ * subcon - the subcon to convert
+ * encoding - the character encoding name (e.g., "utf8"), or None to
+ return raw bytes (usually 8-bit ASCII).
+ """
+ __slots__ = ["encoding"]
+ def __init__(self, subcon, encoding = None):
+ Adapter.__init__(self, subcon)
+ self.encoding = encoding
+ def _encode(self, obj, context):
+ if self.encoding:
+ obj = obj.encode(self.encoding)
+ return obj
+ def _decode(self, obj, context):
+ obj = "".join(obj)
+ if self.encoding:
+ obj = obj.decode(self.encoding)
+ return obj
+
+class PaddedStringAdapter(Adapter):
+ r"""
+ Adapter for padded strings.
+ See String.
+
+ Parameters:
+ * subcon - the subcon to adapt
+ * padchar - the padding character. default is "\x00".
+ * paddir - the direction where padding is placed ("right", "left", or
+ "center"). the default is "right".
+ * trimdir - the direction where trimming will take place ("right" or
+ "left"). the default is "right". trimming is only meaningful for
+ building, when the given string is too long.
+ """
+ __slots__ = ["padchar", "paddir", "trimdir"]
+ def __init__(self, subcon, padchar = "\x00", paddir = "right",
+ trimdir = "right"):
+ if paddir not in ("right", "left", "center"):
+ raise ValueError("paddir must be 'right', 'left' or 'center'",
+ paddir)
+ if trimdir not in ("right", "left"):
+ raise ValueError("trimdir must be 'right' or 'left'", trimdir)
+ Adapter.__init__(self, subcon)
+ self.padchar = padchar
+ self.paddir = paddir
+ self.trimdir = trimdir
+ def _decode(self, obj, context):
+ if self.paddir == "right":
+ obj = obj.rstrip(self.padchar)
+ elif self.paddir == "left":
+ obj = obj.lstrip(self.padchar)
+ else:
+ obj = obj.strip(self.padchar)
+ return obj
+ def _encode(self, obj, context):
+ size = self._sizeof(context)
+ if self.paddir == "right":
+ obj = obj.ljust(size, self.padchar)
+ elif self.paddir == "left":
+ obj = obj.rjust(size, self.padchar)
+ else:
+ obj = obj.center(size, self.padchar)
+ if len(obj) > size:
+ if self.trimdir == "right":
+ obj = obj[:size]
+ else:
+ obj = obj[-size:]
+ return obj
+
+class LengthValueAdapter(Adapter):
+ """
+ Adapter for length-value pairs. It extracts only the value from the
+ pair, and calculates the length based on the value.
+ See PrefixedArray and PascalString.
+
+ Parameters:
+ * subcon - the subcon returning a length-value pair
+ """
+ __slots__ = []
+ def _encode(self, obj, context):
+ return (len(obj), obj)
+ def _decode(self, obj, context):
+ return obj[1]
+
+class CStringAdapter(StringAdapter):
+ r"""
+ Adapter for C-style strings (strings terminated by a terminator char).
+
+ Parameters:
+ * subcon - the subcon to convert
+ * terminators - a sequence of terminator chars. default is "\x00".
+ * encoding - the character encoding to use (e.g., "utf8"), or None to
+ return raw-bytes. the terminator characters are not affected by the
+ encoding.
+ """
+ __slots__ = ["terminators"]
+ def __init__(self, subcon, terminators = "\x00", encoding = None):
+ StringAdapter.__init__(self, subcon, encoding = encoding)
+ self.terminators = terminators
+ def _encode(self, obj, context):
+ return StringAdapter._encode(self, obj, context) + self.terminators[0]
+ def _decode(self, obj, context):
+ return StringAdapter._decode(self, obj[:-1], context)
+
+class TunnelAdapter(Adapter):
+ """
+ Adapter for tunneling (as in protocol tunneling). A tunnel is construct
+ nested upon another (layering). For parsing, the lower layer first parses
+ the data (note: it must return a string!), then the upper layer is called
+ to parse that data (bottom-up). For building it works in a top-down manner;
+ first the upper layer builds the data, then the lower layer takes it and
+ writes it to the stream.
+
+ Parameters:
+ * subcon - the lower layer subcon
+ * inner_subcon - the upper layer (tunneled/nested) subcon
+
+ Example:
+ # a pascal string containing compressed data (zlib encoding), so first
+ # the string is read, decompressed, and finally re-parsed as an array
+ # of UBInt16
+ TunnelAdapter(
+ PascalString("data", encoding = "zlib"),
+ GreedyRange(UBInt16("elements"))
+ )
+ """
+ __slots__ = ["inner_subcon"]
+ def __init__(self, subcon, inner_subcon):
+ Adapter.__init__(self, subcon)
+ self.inner_subcon = inner_subcon
+ def _decode(self, obj, context):
+ return self.inner_subcon._parse(StringIO(obj), context)
+ def _encode(self, obj, context):
+ stream = StringIO()
+ self.inner_subcon._build(obj, stream, context)
+ return stream.getvalue()
+
+class ExprAdapter(Adapter):
+ """
+ A generic adapter that accepts 'encoder' and 'decoder' as parameters. You
+ can use ExprAdapter instead of writing a full-blown class when only a
+ simple expression is needed.
+
+ Parameters:
+ * subcon - the subcon to adapt
+ * encoder - a function that takes (obj, context) and returns an encoded
+ version of obj
+ * decoder - a function that takes (obj, context) and returns an decoded
+ version of obj
+
+ Example:
+ ExprAdapter(UBInt8("foo"),
+ encoder = lambda obj, ctx: obj / 4,
+ decoder = lambda obj, ctx: obj * 4,
+ )
+ """
+ __slots__ = ["_encode", "_decode"]
+ def __init__(self, subcon, encoder, decoder):
+ Adapter.__init__(self, subcon)
+ self._encode = encoder
+ self._decode = decoder
+
+class HexDumpAdapter(Adapter):
+ """
+ Adapter for hex-dumping strings. It returns a HexString, which is a string
+ """
+ __slots__ = ["linesize"]
+ def __init__(self, subcon, linesize = 16):
+ Adapter.__init__(self, subcon)
+ self.linesize = linesize
+ def _encode(self, obj, context):
+ return obj
+ def _decode(self, obj, context):
+ return HexString(obj, linesize = self.linesize)
+
+class ConstAdapter(Adapter):
+ """
+ Adapter for enforcing a constant value ("magic numbers"). When decoding,
+ the return value is checked; when building, the value is substituted in.
+
+ Parameters:
+ * subcon - the subcon to validate
+ * value - the expected value
+
+ Example:
+ Const(Field("signature", 2), "MZ")
+ """
+ __slots__ = ["value"]
+ def __init__(self, subcon, value):
+ Adapter.__init__(self, subcon)
+ self.value = value
+ def _encode(self, obj, context):
+ if obj is None or obj == self.value:
+ return self.value
+ else:
+ raise ConstError("expected %r, found %r" % (self.value, obj))
+ def _decode(self, obj, context):
+ if obj != self.value:
+ raise ConstError("expected %r, found %r" % (self.value, obj))
+ return obj
+
+class SlicingAdapter(Adapter):
+ """
+ Adapter for slicing a list (getting a slice from that list)
+
+ Parameters:
+ * subcon - the subcon to slice
+ * start - start index
+ * stop - stop index (or None for up-to-end)
+ * step - step (or None for every element)
+ """
+ __slots__ = ["start", "stop", "step"]
+ def __init__(self, subcon, start, stop = None):
+ Adapter.__init__(self, subcon)
+ self.start = start
+ self.stop = stop
+ def _encode(self, obj, context):
+ if self.start is None:
+ return obj
+ return [None] * self.start + obj
+ def _decode(self, obj, context):
+ return obj[self.start:self.stop]
+
+class IndexingAdapter(Adapter):
+ """
+ Adapter for indexing a list (getting a single item from that list)
+
+ Parameters:
+ * subcon - the subcon to index
+ * index - the index of the list to get
+ """
+ __slots__ = ["index"]
+ def __init__(self, subcon, index):
+ Adapter.__init__(self, subcon)
+ if type(index) is not int:
+ raise TypeError("index must be an integer", type(index))
+ self.index = index
+ def _encode(self, obj, context):
+ return [None] * self.index + [obj]
+ def _decode(self, obj, context):
+ return obj[self.index]
+
+class PaddingAdapter(Adapter):
+ r"""
+ Adapter for padding.
+
+ Parameters:
+ * subcon - the subcon to pad
+ * pattern - the padding pattern (character). default is "\x00"
+ * strict - whether or not to verify, during parsing, that the given
+ padding matches the padding pattern. default is False (unstrict)
+ """
+ __slots__ = ["pattern", "strict"]
+ def __init__(self, subcon, pattern = "\x00", strict = False):
+ Adapter.__init__(self, subcon)
+ self.pattern = pattern
+ self.strict = strict
+ def _encode(self, obj, context):
+ return self._sizeof(context) * self.pattern
+ def _decode(self, obj, context):
+ if self.strict:
+ expected = self._sizeof(context) * self.pattern
+ if obj != expected:
+ raise PaddingError("expected %r, found %r" % (expected, obj))
+ return obj
+
+
+#===============================================================================
+# validators
+#===============================================================================
+class Validator(Adapter):
+ """
+ Abstract class: validates a condition on the encoded/decoded object.
+ Override _validate(obj, context) in deriving classes.
+
+ Parameters:
+ * subcon - the subcon to validate
+ """
+ __slots__ = []
+ def _decode(self, obj, context):
+ if not self._validate(obj, context):
+ raise ValidationError("invalid object", obj)
+ return obj
+ def _encode(self, obj, context):
+ return self._decode(obj, context)
+ def _validate(self, obj, context):
+ raise NotImplementedError()
+
+class OneOf(Validator):
+ """
+ Validates that the object is one of the listed values.
+
+ :param ``Construct`` subcon: object to validate
+ :param iterable valids: a set of valid values
+
+ >>> OneOf(UBInt8("foo"), [4,5,6,7]).parse("\\x05")
+ 5
+ >>> OneOf(UBInt8("foo"), [4,5,6,7]).parse("\\x08")
+ Traceback (most recent call last):
+ ...
+ construct.core.ValidationError: ('invalid object', 8)
+ >>>
+ >>> OneOf(UBInt8("foo"), [4,5,6,7]).build(5)
+ '\\x05'
+ >>> OneOf(UBInt8("foo"), [4,5,6,7]).build(9)
+ Traceback (most recent call last):
+ ...
+ construct.core.ValidationError: ('invalid object', 9)
+ """
+ __slots__ = ["valids"]
+ def __init__(self, subcon, valids):
+ Validator.__init__(self, subcon)
+ self.valids = valids
+ def _validate(self, obj, context):
+ return obj in self.valids
+
+class NoneOf(Validator):
+ """
+ Validates that the object is none of the listed values.
+
+ :param ``Construct`` subcon: object to validate
+ :param iterable invalids: a set of invalid values
+
+ >>> NoneOf(UBInt8("foo"), [4,5,6,7]).parse("\\x08")
+ 8
+ >>> NoneOf(UBInt8("foo"), [4,5,6,7]).parse("\\x06")
+ Traceback (most recent call last):
+ ...
+ construct.core.ValidationError: ('invalid object', 6)
+ """
+ __slots__ = ["invalids"]
+ def __init__(self, subcon, invalids):
+ Validator.__init__(self, subcon)
+ self.invalids = invalids
+ def _validate(self, obj, context):
+ return obj not in self.invalids
View
1,322 construct/core.py
@@ -0,0 +1,1322 @@
+from struct import Struct as Packer
+
+from lib import StringIO
+from lib import Container, ListContainer, LazyContainer
+
+
+#===============================================================================
+# exceptions
+#===============================================================================
+class ConstructError(Exception):
+ __slots__ = []
+class FieldError(ConstructError):
+ __slots__ = []
+class SizeofError(ConstructError):
+ __slots__ = []
+class AdaptationError(ConstructError):
+ __slots__ = []
+class ArrayError(ConstructError):
+ __slots__ = []
+class RangeError(ConstructError):
+ __slots__ = []
+class SwitchError(ConstructError):
+ __slots__ = []
+class SelectError(ConstructError):
+ __slots__ = []
+class TerminatorError(ConstructError):
+ __slots__ = []
+
+#===============================================================================
+# abstract constructs
+#===============================================================================
+class Construct(object):
+ """
+ The mother of all constructs.
+
+ This object is generally not directly instantiated, and it does not
+ directly implement parsing and building, so it is largely only of interest
+ to subclass implementors.
+
+ The external user API:
+
+ * parse()
+ * parse_stream()
+ * build()
+ * build_stream()
+ * sizeof()
+
+ Subclass authors should not override the external methods. Instead,
+ another API is available:
+
+ * _parse()
+ * _build()
+ * _sizeof()
+
+ There is also a flag API:
+
+ * _set_flag()
+ * _clear_flag()
+ * _inherit_flags()
+ * _is_flag()
+
+ And stateful copying:
+
+ * __getstate__()
+ * __setstate__()
+
+ Attributes and Inheritance
+ ==========================
+
+ All constructs have a name and flags. The name is used for naming struct
+ members and context dictionaries. Note that the name can either be a
+ string, or None if the name is not needed. A single underscore ("_") is a
+ reserved name, and so are names starting with a less-than character ("<").
+ The name should be descriptive, short, and valid as a Python identifier,
+ although these rules are not enforced.
+
+ The flags specify additional behavioral information about this construct.
+ Flags are used by enclosing constructs to determine a proper course of
+ action. Flags are inherited by default, from inner subconstructs to outer
+ constructs. The enclosing construct may set new flags or clear existing
+ ones, as necessary.
+
+ For example, if FLAG_COPY_CONTEXT is set, repeaters will pass a copy of
+ the context for each iteration, which is necessary for OnDemand parsing.
+ """
+
+ FLAG_COPY_CONTEXT = 0x0001
+ FLAG_DYNAMIC = 0x0002
+ FLAG_EMBED = 0x0004
+ FLAG_NESTING = 0x0008
+
+ __slots__ = ["name", "conflags"]
+ def __init__(self, name, flags = 0):
+ if name is not None:
+ if type(name) is not str:
+ raise TypeError("name must be a string or None", name)
+ if name == "_" or name.startswith("<"):
+ raise ValueError("reserved name", name)
+ self.name = name
+ self.conflags = flags
+
+ def __repr__(self):
+ return "%s(%r)" % (self.__class__.__name__, self.name)
+
+ def _set_flag(self, flag):
+ """
+ Set the given flag or flags.
+
+ :param int flag: flag to set; may be OR'd combination of flags
+ """
+
+ self.conflags |= flag
+
+ def _clear_flag(self, flag):
+ """
+ Clear the given flag or flags.
+
+ :param int flag: flag to clear; may be OR'd combination of flags
+ """
+
+ self.conflags &= ~flag
+
+ def _inherit_flags(self, *subcons):
+ """
+ Pull flags from subconstructs.
+ """
+
+ for sc in subcons:
+ self._set_flag(sc.conflags)
+
+ def _is_flag(self, flag):
+ """
+ Check whether a given flag is set.
+
+ :param int flag: flag to check
+ """
+
+ return bool(self.conflags & flag)
+
+ def __getstate__(self):
+ """
+ Obtain a dictionary representing this construct's state.
+ """
+
+ attrs = {}
+ if hasattr(self, "__dict__"):
+ attrs.update(self.__dict__)
+ slots = []
+ c = self.__class__
+ while c is not None:
+ if hasattr(c, "__slots__"):
+ slots.extend(c.__slots__)
+ c = c.__base__
+ for name in slots:
+ if hasattr(self, name):
+ attrs[name] = getattr(self, name)
+ return attrs
+
+ def __setstate__(self, attrs):
+ """
+ Set this construct's state to a given state.
+ """
+
+ for name, value in attrs.iteritems():
+ setattr(self, name, value)
+
+ def __copy__(self):
+ """returns a copy of this construct"""
+ self2 = object.__new__(self.__class__)
+ self2.__setstate__(self.__getstate__())
+ return self2
+
+ def parse(self, data):
+ """
+ Parse an in-memory buffer.
+
+ Strings, buffers, memoryviews, and other complete buffers can be
+ parsed with this method.
+ """
+
+ return self.parse_stream(StringIO(data))
+
+ def parse_stream(self, stream):
+ """
+ Parse a stream.
+
+ Files, pipes, sockets, and other streaming sources of data are handled
+ by this method.
+ """
+
+ return self._parse(stream, Container())
+
+ def _parse(self, stream, context):
+ """
+ Override me in your subclass.
+ """
+
+ raise NotImplementedError()
+
+ def build(self, obj):
+ """
+ Build an object in memory.
+ """
+
+ stream = StringIO()
+ self.build_stream(obj, stream)
+ return stream.getvalue()
+
+ def build_stream(self, obj, stream):
+ """
+ Build an object directly into a stream.
+ """
+
+ self._build(obj, stream, Container())
+
+ def _build(self, obj, stream, context):
+ """
+ Override me in your subclass.
+ """
+
+ raise NotImplementedError()
+
+ def sizeof(self, context=None):
+ """
+ Calculate the size of this object, optionally using a context.
+
+ Some constructs have no fixed size and can only know their size for a
+ given hunk of data; these constructs will raise an error if they are
+ not passed a context.
+
+ :param ``Container`` context: contextual data
+
+ :returns: int of the length of this construct
+ :raises SizeofError: the size could not be determined
+ """
+
+ if context is None:
+ context = Container()
+ try:
+ return self._sizeof(context)
+ except Exception, e:
+ raise SizeofError(e)
+
+ def _sizeof(self, context):
+ """
+ Override me in your subclass.
+ """
+
+ raise SizeofError("Raw Constructs have no size!")
+
+class Subconstruct(Construct):
+ """
+ Abstract subconstruct (wraps an inner construct, inheriting it's
+ name and flags).
+
+ Parameters:
+ * subcon - the construct to wrap
+ """
+ __slots__ = ["subcon"]
+ def __init__(self, subcon):
+ Construct.__init__(self, subcon.name, subcon.conflags)
+ self.subcon = subcon
+ def _parse(self, stream, context):
+ return self.subcon._parse(stream, context)
+ def _build(self, obj, stream, context):
+ self.subcon._build(obj, stream, context)
+ def _sizeof(self, context):
+ return self.subcon._sizeof(context)
+
+class Adapter(Subconstruct):
+ """
+ Abstract adapter: calls _decode for parsing and _encode for building.
+
+ Parameters:
+ * subcon - the construct to wrap
+ """
+ __slots__ = []
+ def _parse(self, stream, context):
+ return self._decode(self.subcon._parse(stream, context), context)
+ def _build(self, obj, stream, context):
+ self.subcon._build(self._encode(obj, context), stream, context)
+ def _decode(self, obj, context):
+ raise NotImplementedError()
+ def _encode(self, obj, context):
+ raise NotImplementedError()
+
+
+#===============================================================================
+# Fields
+#===============================================================================
+def _read_stream(stream, length):
+ if length < 0:
+ raise ValueError("length must be >= 0", length)
+ data = stream.read(length)
+ if len(data) != length:
+ raise FieldError("expected %d, found %d" % (length, len(data)))
+ return data
+
+def _write_stream(stream, length, data):
+ if length < 0:
+ raise ValueError("length must be >= 0", length)
+ if len(data) != length:
+ raise FieldError("expected %d, found %d" % (length, len(data)))
+ stream.write(data)
+
+class StaticField(Construct):
+ """
+ A fixed-size byte field.
+
+ :param str name: field name
+ :param int length: number of bytes in the field
+ """
+
+ __slots__ = ["length"]
+ def __init__(self, name, length):
+ Construct.__init__(self, name)
+ self.length = length
+ def _parse(self, stream, context):
+ return _read_stream(stream, self.length)
+ def _build(self, obj, stream, context):
+ _write_stream(stream, self.length, obj)
+ def _sizeof(self, context):
+ return self.length
+
+class FormatField(StaticField):
+ """
+ A field that uses ``struct`` to pack and unpack data.
+
+ See ``struct`` documentation for instructions on crafting format strings.
+
+ :param str name: name of the field
+ :param str endianness: format endianness string; one of "<", ">", or "="
+ :param str format: a single format character
+ """
+
+ __slots__ = ["packer"]
+ def __init__(self, name, endianity, format):
+ if endianity not in (">", "<", "="):
+ raise ValueError("endianity must be be '=', '<', or '>'",
+ endianity)
+ if len(format) != 1:
+ raise ValueError("must specify one and only one format char")
+ self.packer = Packer(endianity + format)
+ StaticField.__init__(self, name, self.packer.size)
+ def __getstate__(self):
+ attrs = StaticField.__getstate__(self)
+ attrs["packer"] = attrs["packer"].format
+ return attrs
+ def __setstate__(self, attrs):
+ attrs["packer"] = Packer(attrs["packer"])
+ return StaticField.__setstate__(attrs)
+ def _parse(self, stream, context):
+ try:
+ return self.packer.unpack(_read_stream(stream, self.length))[0]
+ except Exception, ex:
+ raise FieldError(ex)
+ def _build(self, obj, stream, context):
+ try:
+ _write_stream(stream, self.length, self.packer.pack(obj))
+ except Exception, ex:
+ raise FieldError(ex)
+
+class MetaField(Construct):
+ """
+ A variable-length field. The length is obtained at runtime from a
+ function.
+
+ :param str name: name of the field
+ :param callable lengthfunc: callable that takes a context and returns
+ length as an int
+
+ >>> foo = Struct("foo",
+ ... Byte("length"),
+ ... MetaField("data", lambda ctx: ctx["length"])
+ ... )
+ >>> foo.parse("\\x03ABC")
+ Container(data = 'ABC', length = 3)
+ >>> foo.parse("\\x04ABCD")
+ Container(data = 'ABCD', length = 4)
+ """
+
+ __slots__ = ["lengthfunc"]
+ def __init__(self, name, lengthfunc):
+ Construct.__init__(self, name)
+ self.lengthfunc = lengthfunc
+ self._set_flag(self.FLAG_DYNAMIC)
+ def _parse(self, stream, context):
+ return _read_stream(stream, self.lengthfunc(context))
+ def _build(self, obj, stream, context):
+ _write_stream(stream, self.lengthfunc(context), obj)
+ def _sizeof(self, context):
+ return self.lengthfunc(context)
+
+
+#===============================================================================
+# arrays and repeaters
+#===============================================================================
+class MetaArray(Subconstruct):
+ """
+ An array (repeater) of a meta-count. The array will iterate exactly
+ `countfunc()` times. Will raise ArrayError if less elements are found.
+ See also Array, Range and RepeatUntil.
+
+ Parameters:
+ * countfunc - a function that takes the context as a parameter and returns
+ the number of elements of the array (count)
+ * subcon - the subcon to repeat `countfunc()` times
+
+ Example:
+ MetaArray(lambda ctx: 5, UBInt8("foo"))
+ """
+ __slots__ = ["countfunc"]
+ def __init__(self, countfunc, subcon):
+ Subconstruct.__init__(self, subcon)
+ self.countfunc = countfunc
+ self._clear_flag(self.FLAG_COPY_CONTEXT)
+ self._set_flag(self.FLAG_DYNAMIC)
+ def _parse(self, stream, context):
+ obj = ListContainer()
+ c = 0
+ count = self.countfunc(context)
+ try:
+ if self.subcon.conflags & self.FLAG_COPY_CONTEXT:
+ while c < count:
+ obj.append(self.subcon._parse(stream, context.__copy__()))
+ c += 1
+ else:
+ while c < count:
+ obj.append(self.subcon._parse(stream, context))
+ c += 1
+ except ConstructError, ex:
+ raise ArrayError("expected %d, found %d" % (count, c), ex)
+ return obj
+ def _build(self, obj, stream, context):
+ count = self.countfunc(context)
+ if len(obj) != count:
+ raise ArrayError("expected %d, found %d" % (count, len(obj)))
+ if self.subcon.conflags & self.FLAG_COPY_CONTEXT:
+ for subobj in obj:
+ self.subcon._build(subobj, stream, context.__copy__())
+ else:
+ for subobj in obj:
+ self.subcon._build(subobj, stream, context)
+ def _sizeof(self, context):
+ return self.subcon._sizeof(context) * self.countfunc(context)
+
+class Range(Subconstruct):
+ """
+ A range-array. The subcon will iterate between `mincount` to `maxcount`
+ times. If less than `mincount` elements are found, raises RangeError.
+ See also GreedyRange and OptionalGreedyRange.
+
+ The general-case repeater. Repeats the given unit for at least mincount
+ times, and up to maxcount times. If an exception occurs (EOF, validation
+ error), the repeater exits. If less than mincount units have been
+ successfully parsed, a RangeError is raised.
+
+ .. note::
+ This object requires a seekable stream for parsing.
+
+ :param int mincount: the minimal count
+ :param int maxcount: the maximal count
+ :param Construct subcon: the subcon to repeat
+
+ >>> c = Range(3, 7, UBInt8("foo"))
+ >>> c.parse("\\x01\\x02")
+ Traceback (most recent call last):
+ ...
+ construct.core.RangeError: expected 3..7, found 2
+ >>> c.parse("\\x01\\x02\\x03")
+ [1, 2, 3]
+ >>> c.parse("\\x01\\x02\\x03\\x04\\x05\\x06")
+ [1, 2, 3, 4, 5, 6]
+ >>> c.parse("\\x01\\x02\\x03\\x04\\x05\\x06\\x07")
+ [1, 2, 3, 4, 5, 6, 7]
+ >>> c.parse("\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09")
+ [1, 2, 3, 4, 5, 6, 7]
+ >>> c.build([1,2])
+ Traceback (most recent call last):
+ ...
+ construct.core.RangeError: expected 3..7, found 2
+ >>> c.build([1,2,3,4])
+ '\\x01\\x02\\x03\\x04'
+ >>> c.build([1,2,3,4,5,6,7,8])
+ Traceback (most recent call last):
+ ...
+ construct.core.RangeError: expected 3..7, found 8
+ """
+
+ __slots__ = ["mincount", "maxcout"]
+ def __init__(self, mincount, maxcout, subcon):
+ Subconstruct.__init__(self, subcon)
+ self.mincount = mincount
+ self.maxcout = maxcout
+ self._clear_flag(self.FLAG_COPY_CONTEXT)
+ self._set_flag(self.FLAG_DYNAMIC)
+ def _parse(self, stream, context):
+ obj = ListContainer()
+ c = 0
+ try:
+ if self.subcon.conflags & self.FLAG_COPY_CONTEXT:
+ while c < self.maxcout:
+ pos = stream.tell()
+ obj.append(self.subcon._parse(stream, context.__copy__()))
+ c += 1
+ else:
+ while c < self.maxcout:
+ pos = stream.tell()
+ obj.append(self.subcon._parse(stream, context))
+ c += 1
+ except ConstructError, ex:
+ if c < self.mincount:
+ raise RangeError("expected %d to %d, found %d" %
+ (self.mincount, self.maxcout, c), ex)
+ stream.seek(pos)
+ return obj
+ def _build(self, obj, stream, context):
+ if len(obj) < self.mincount or len(obj) > self.maxcout:
+ raise RangeError("expected %d to %d, found %d" %
+ (self.mincount, self.maxcout, len(obj)))
+ cnt = 0
+ try:
+ if self.subcon.conflags & self.FLAG_COPY_CONTEXT:
+ for subobj in obj:
+ self.subcon._build(subobj, stream, context.__copy__())
+ cnt += 1
+ else:
+ for subobj in obj:
+ self.subcon._build(subobj, stream, context)
+ cnt += 1
+ except ConstructError, ex:
+ if cnt < self.mincount:
+ raise RangeError("expected %d to %d, found %d" %
+ (self.mincount, self.maxcout, len(obj)), ex)
+ def _sizeof(self, context):
+ raise SizeofError("can't calculate size")
+
+class RepeatUntil(Subconstruct):
+ """
+ An array that repeat until the predicate indicates it to stop. Note that
+ the last element (which caused the repeat to exit) is included in the
+ return value.
+
+ Parameters:
+ * predicate - a predicate function that takes (obj, context) and returns
+ True if the stop-condition is met, or False to continue.
+ * subcon - the subcon to repeat.
+
+ Example:
+ # will read chars until \x00 (inclusive)
+ RepeatUntil(lambda obj, ctx: obj == "\x00",
+ Field("chars", 1)
+ )
+ """
+ __slots__ = ["predicate"]
+ def __init__(self, predicate, subcon):
+ Subconstruct.__init__(self, subcon)
+ self.predicate = predicate
+ self._clear_flag(self.FLAG_COPY_CONTEXT)
+ self._set_flag(self.FLAG_DYNAMIC)
+ def _parse(self, stream, context):
+ obj = []
+ try:
+ if self.subcon.conflags & self.FLAG_COPY_CONTEXT:
+ while True:
+ subobj = self.subcon._parse(stream, context.__copy__())
+ obj.append(subobj)
+ if self.predicate(subobj, context):
+ break
+ else:
+ while True:
+ subobj = self.subcon._parse(stream, context)
+ obj.append(subobj)
+ if self.predicate(subobj, context):
+ break
+ except ConstructError, ex:
+ raise ArrayError("missing terminator", ex)
+ return obj
+ def _build(self, obj, stream, context):
+ terminated = False
+ if self.subcon.conflags & self.FLAG_COPY_CONTEXT:
+ for subobj in obj:
+ self.subcon._build(subobj, stream, context.__copy__())
+ if self.predicate(subobj, context):
+ terminated = True
+ break
+ else:
+ for subobj in obj:
+ self.subcon._build(subobj, stream, context.__copy__())
+ if self.predicate(subobj, context):
+ terminated = True
+ break
+ if not terminated:
+ raise ArrayError("missing terminator")
+ def _sizeof(self, context):
+ raise SizeofError("can't calculate size")
+
+
+#===============================================================================
+# structures and sequences
+#===============================================================================
+class Struct(Construct):
+ """
+ A sequence of named constructs, similar to structs in C. The elements are
+ parsed and built in the order they are defined.
+ See also Embedded.
+
+ Parameters:
+ * name - the name of the structure
+ * subcons - a sequence of subconstructs that make up this structure.
+ * nested - a keyword-only argument that indicates whether this struct
+ creates a nested context. The default is True. This parameter is
+ considered "advanced usage", and may be removed in the future.
+
+ Example:
+ Struct("foo",
+ UBInt8("first_element"),
+ UBInt16("second_element"),
+ Padding(2),
+ UBInt8("third_element"),
+ )
+ """
+ __slots__ = ["subcons", "nested"]
+ def __init__(self, name, *subcons, **kw):
+ self.nested = kw.pop("nested", True)
+ if kw:
+ raise TypeError("the only keyword argument accepted is 'nested'", kw)
+ Construct.__init__(self, name)
+ self.subcons = subcons
+ self._inherit_flags(*subcons)
+ self._clear_flag(self.FLAG_EMBED)
+ def _parse(self, stream, context):
+ if "<obj>" in context:
+ obj = context["<obj>"]
+ del context["<obj>"]
+ else:
+ obj = Container()
+ if self.nested:
+ context = Container(_ = context)
+ for sc in self.subcons:
+ if sc.conflags & self.FLAG_EMBED:
+ context["<obj>"] = obj
+ sc._parse(stream, context)
+ else:
+ subobj = sc._parse(stream, context)
+ if sc.name is not None:
+ obj[sc.name] = subobj
+ context[sc.name] = subobj
+ return obj
+ def _build(self, obj, stream, context):
+ if "<unnested>" in context:
+ del context["<unnested>"]
+ elif self.nested:
+ context = Container(_ = context)
+ for sc in self.subcons:
+ if sc.conflags & self.FLAG_EMBED:
+ context["<unnested>"] = True
+ subobj = obj
+ elif sc.name is None:
+ subobj = None
+ else:
+ subobj = getattr(obj, sc.name)
+ context[sc.name] = subobj
+ sc._build(subobj, stream, context)
+ def _sizeof(self, context):
+ if self.nested:
+ context = Container(_ = context)
+ return sum(sc._sizeof(context) for sc in self.subcons)
+
+class Sequence(Struct):
+ """
+ A sequence of unnamed constructs. The elements are parsed and built in the
+ order they are defined.
+ See also Embedded.
+
+ Parameters:
+ * name - the name of the structure
+ * subcons - a sequence of subconstructs that make up this structure.
+ * nested - a keyword-only argument that indicates whether this struct
+ creates a nested context. The default is True. This parameter is
+ considered "advanced usage", and may be removed in the future.
+
+ Example:
+ Sequence("foo",
+ UBInt8("first_element"),
+ UBInt16("second_element"),
+ Padding(2),
+ UBInt8("third_element"),
+ )
+ """
+ __slots__ = []
+ def _parse(self, stream, context):
+ if "<obj>" in context:
+ obj = context["<obj>"]
+ del context["<obj>"]
+ else:
+ obj = ListContainer()
+ if self.nested:
+ context = Container(_ = context)
+ for sc in self.subcons:
+ if sc.conflags & self.FLAG_EMBED:
+ context["<obj>"] = obj
+ sc._parse(stream, context)
+ else:
+ subobj = sc._parse(stream, context)
+ if sc.name is not None:
+ obj.append(subobj)
+ context[sc.name] = subobj
+ return obj
+ def _build(self, obj, stream, context):
+ if "<unnested>" in context:
+ del context["<unnested>"]
+ elif self.nested:
+ context = Container(_ = context)
+ objiter = iter(obj)
+ for sc in self.subcons:
+ if sc.conflags & self.FLAG_EMBED:
+ context["<unnested>"] = True
+ subobj = objiter
+ elif sc.name is None:
+ subobj = None
+ else:
+ subobj = objiter.next()
+ context[sc.name] = subobj
+ sc._build(subobj, stream, context)
+
+class Union(Construct):
+ """
+ a set of overlapping fields (like unions in C). when parsing,
+ all fields read the same data; when building, only the first subcon
+ (called "master") is used.
+
+ Parameters:
+ * name - the name of the union
+ * master - the master subcon, i.e., the subcon used for building and
+ calculating the total size
+ * subcons - additional subcons
+
+ Example:
+ Union("what_are_four_bytes",
+ UBInt32("one_dword"),
+ Struct("two_words", UBInt16("first"), UBInt16("second")),
+ Struct("four_bytes",
+ UBInt8("a"),
+ UBInt8("b"),
+ UBInt8("c"),
+ UBInt8("d")
+ ),
+ )
+ """
+ __slots__ = ["parser", "builder"]
+ def __init__(self, name, master, *subcons, **kw):
+ Construct.__init__(self, name)
+ args = [Peek(sc) for sc in subcons]
+ args.append(MetaField(None, lambda ctx: master._sizeof(ctx)))
+ self.parser = Struct(name, Peek(master, perform_build = True), *args)
+ self.builder = Struct(name, master)
+ def _parse(self, stream, context):
+ return self.parser._parse(stream, context)
+ def _build(self, obj, stream, context):
+ return self.builder._build(obj, stream, context)
+ def _sizeof(self, context):
+ return self.builder._sizeof(context)
+
+#===============================================================================
+# conditional
+#===============================================================================
+class Switch(Construct):
+ """
+ A conditional branch. Switch will choose the case to follow based on
+ the return value of keyfunc. If no case is matched, and no default value
+ is given, SwitchError will be raised.
+ See also Pass.
+
+ Parameters:
+ * name - the name of the construct
+ * keyfunc - a function that takes the context and returns a key, which
+ will ne used to choose the relevant case.
+ * cases - a dictionary mapping keys to constructs. the keys can be any
+ values that may be returned by keyfunc.
+ * default - a default value to use when the key is not found in the cases.
+ if not supplied, an exception will be raised when the key is not found.
+ You can use the builtin construct Pass for 'do-nothing'.
+ * include_key - whether or not to include the key in the return value
+ of parsing. defualt is False.
+
+ Example:
+ Struct("foo",
+ UBInt8("type"),
+ Switch("value", lambda ctx: ctx.type, {
+ 1 : UBInt8("spam"),
+ 2 : UBInt16("spam"),
+ 3 : UBInt32("spam"),
+ 4 : UBInt64("spam"),
+ }
+ ),
+ )
+ """
+
+ class NoDefault(Construct):
+ def _parse(self, stream, context):
+ raise SwitchError("no default case defined")
+ def _build(self, obj, stream, context):
+ raise SwitchError("no default case defined")
+ def _sizeof(self, context):
+ raise SwitchError("no default case defined")
+ NoDefault = NoDefault("No default value specified")
+
+ __slots__ = ["subcons", "keyfunc", "cases", "default", "include_key"]
+
+ def __init__(self, name, keyfunc, cases, default = NoDefault,
+ include_key = False):
+ Construct.__init__(self, name)
+ self._inherit_flags(*cases.values())
+ self.keyfunc = keyfunc
+ self.cases = cases
+ self.default = default
+ self.include_key = include_key
+ self._inherit_flags(*cases.values())
+ self._set_flag(self.FLAG_DYNAMIC)
+ def _parse(self, stream, context):
+ key = self.keyfunc(context)
+ obj = self.cases.get(key, self.default)._parse(stream, context)
+ if self.include_key:
+ return key, obj
+ else:
+ return obj
+ def _build(self, obj, stream, context):
+ if self.include_key:
+ key, obj = obj
+ else:
+ key = self.keyfunc(context)
+ case = self.cases.get(key, self.default)
+ case._build(obj, stream, context)
+ def _sizeof(self, context):
+ case = self.cases.get(self.keyfunc(context), self.default)
+ return case._sizeof(context)
+
+class Select(Construct):
+ """
+ Selects the first matching subconstruct. It will literally try each of
+ the subconstructs, until one matches.
+
+ Notes:
+ * requires a seekable stream.
+
+ Parameters:
+ * name - the name of the construct
+ * subcons - the subcons to try (order-sensitive)
+ * include_name - a keyword only argument, indicating whether to include
+ the name of the selected subcon in the return value of parsing. default
+ is false.
+
+ Example:
+ Select("foo",
+ UBInt64("large"),
+ UBInt32("medium"),
+ UBInt16("small"),
+ UBInt8("tiny"),
+ )
+ """
+ __slots__ = ["subcons", "include_name"]
+ def __init__(self, name, *subcons, **kw):
+ include_name = kw.pop("include_name", False)
+ if kw:
+ raise TypeError("the only keyword argument accepted "
+ "is 'include_name'", kw)
+ Construct.__init__(self, name)
+ self.subcons = subcons
+ self.include_name = include_name
+ self._inherit_flags(*subcons)
+ self._set_flag(self.FLAG_DYNAMIC)
+ def _parse(self, stream, context):
+ for sc in self.subcons:
+ pos = stream.tell()
+ context2 = context.__copy__()
+ try:
+ obj = sc._parse(stream, context2)
+ except ConstructError:
+ stream.seek(pos)
+ else:
+ context.__update__(context2)
+ if self.include_name:
+ return sc.name, obj
+ else:
+ return obj
+ raise SelectError("no subconstruct matched")
+ def _build(self, obj, stream, context):
+ if self.include_name:
+ name, obj = obj
+ for sc in self.subcons:
+ if sc.name == name:
+ sc._build(obj, stream, context)
+ return
+ else:
+ for sc in self.subcons:
+ stream2 = StringIO()
+ context2 = context.__copy__()
+ try:
+ sc._build(obj, stream2, context2)
+ except Exception:
+ pass
+ else:
+ context.__update__(context2)
+ stream.write(stream2.getvalue())
+ return
+ raise SelectError("no subconstruct matched", obj)
+ def _sizeof(self, context):
+ raise SizeofError("can't calculate size")
+
+
+#===============================================================================
+# stream manipulation
+#===============================================================================
+class Pointer(Subconstruct):
+ """
+ Changes the stream position to a given offset, where the construction
+ should take place, and restores the stream position when finished.
+ See also Anchor, OnDemand and OnDemandPointer.
+
+ Notes:
+ * requires a seekable stream.
+
+ Parameters:
+ * offsetfunc: a function that takes the context and returns an absolute
+ stream position, where the construction would take place
+ * subcon - the subcon to use at `offsetfunc()`
+
+ Example:
+ Struct("foo",
+ UBInt32("spam_pointer"),
+ Pointer(lambda ctx: ctx.spam_pointer,
+ Array(5, UBInt8("spam"))
+ )
+ )
+ """
+ __slots__ = ["offsetfunc"]
+ def __init__(self, offsetfunc, subcon):
+ Subconstruct.__init__(self, subcon)
+ self.offsetfunc = offsetfunc
+ def _parse(self, stream, context):
+ newpos = self.offsetfunc(context)
+ origpos = stream.tell()
+ stream.seek(newpos)
+ obj = self.subcon._parse(stream, context)
+ stream.seek(origpos)
+ return obj
+ def _build(self, obj, stream, context):
+ newpos = self.offsetfunc(context)
+ origpos = stream.tell()
+ stream.seek(newpos)
+ self.subcon._build(obj, stream, context)
+ stream.seek(origpos)
+ def _sizeof(self, context):
+ return 0
+
+class Peek(Subconstruct):
+ """
+ Peeks at the stream: parses without changing the stream position.
+ See also Union. If the end of the stream is reached when peeking,
+ returns None.
+
+ Notes:
+ * requires a seekable stream.
+
+ Parameters:
+ * subcon - the subcon to peek at
+ * perform_build - whether or not to perform building. by default this
+ parameter is set to False, meaning building is a no-op.
+
+ Example:
+ Peek(UBInt8("foo"))
+ """
+ __slots__ = ["perform_build"]
+ def __init__(self, subcon, perform_build = False):
+ Subconstruct.__init__(self, subcon)
+ self.perform_build = perform_build
+ def _parse(self, stream, context):
+ pos = stream.tell()
+ try:
+ return self.subcon._parse(stream, context)
+ except FieldError:
+ pass
+ finally:
+ stream.seek(pos)
+ def _build(self, obj, stream, context):
+ if self.perform_build:
+ self.subcon._build(obj, stream, context)
+ def _sizeof(self, context):
+ return 0
+
+class OnDemand(Subconstruct):
+ """
+ Allows for on-demand (lazy) parsing. When parsing, it will return a
+ LazyContainer that represents a pointer to the data, but does not actually
+ parses it from stream until it's "demanded".
+ By accessing the 'value' property of LazyContainers, you will demand the
+ data from the stream. The data will be parsed and cached for later use.
+ You can use the 'has_value' property to know whether the data has already
+ been demanded.
+ See also OnDemandPointer.
+
+ Notes:
+ * requires a seekable stream.
+
+ Parameters:
+ * subcon -
+ * advance_stream - whether or not to advance the stream position. by
+ default this is True, but if subcon is a pointer, this should be False.
+ * force_build - whether or not to force build. If set to False, and the
+ LazyContainer has not been demaned, building is a no-op.
+
+ Example:
+ OnDemand(Array(10000, UBInt8("foo"))
+ """
+ __slots__ = ["advance_stream", "force_build"]
+ def __init__(self, subcon, advance_stream = True, force_build = True):
+ Subconstruct.__init__(self, subcon)
+ self.advance_stream = advance_stream
+ self.force_build = force_build
+ def _parse(self, stream, context):
+ obj = LazyContainer(self.subcon, stream, stream.tell(), context)
+ if self.advance_stream:
+ stream.seek(self.subcon._sizeof(context), 1)
+ return obj
+ def _build(self, obj, stream, context):
+ if not isinstance(obj, LazyContainer):
+ self.subcon._build(obj, stream, context)
+ elif self.force_build or obj.has_value:
+ self.subcon._build(obj.value, stream, context)
+ elif self.advance_stream:
+ stream.seek(self.subcon._sizeof(context), 1)
+
+class Buffered(Subconstruct):
+ """
+ Creates an in-memory buffered stream, which can undergo encoding and
+ decoding prior to being passed on to the subconstruct.
+ See also Bitwise.
+
+ Note:
+ * Do not use pointers inside Buffered
+
+ Parameters:
+ * subcon - the subcon which will operate on the buffer
+ * encoder - a function that takes a string and returns an encoded
+ string (used after building)
+ * decoder - a function that takes a string and returns a decoded
+ string (used before parsing)
+ * resizer - a function that takes the size of the subcon and "adjusts"
+ or "resizes" it according to the encoding/decoding process.
+
+ Example:
+ Buffered(BitField("foo", 16),
+ encoder = decode_bin,
+ decoder = encode_bin,
+ resizer = lambda size: size / 8,
+ )
+ """
+ __slots__ = ["encoder", "decoder", "resizer"]
+ def __init__(self, subcon, decoder, encoder, resizer):
+ Subconstruct.__init__(self, subcon)
+ self.encoder = encoder
+ self.decoder = decoder
+ self.resizer = resizer
+ def _parse(self, stream, context):
+ data = _read_stream(stream, self._sizeof(context))
+ stream2 = StringIO(self.decoder(data))
+ return self.subcon._parse(stream2, context)
+ def _build(self, obj, stream, context):
+ size = self._sizeof(context)
+ stream2 = StringIO()
+ self.subcon._build(obj, stream2, context)
+ data = self.encoder(stream2.getvalue())
+ assert len(data) == size
+ _write_stream(stream, self._sizeof(context), data)
+ def _sizeof(self, context):
+ return self.resizer(self.subcon._sizeof(context))
+
+class Restream(Subconstruct):
+ """
+ Wraps the stream with a read-wrapper (for parsing) or a
+ write-wrapper (for building). The stream wrapper can buffer the data
+ internally, reading it from- or writing it to the underlying stream
+ as needed. For example, BitStreamReader reads whole bytes from the
+ underlying stream, but returns them as individual bits.
+ See also Bitwise.
+
+ When the parsing or building is done, the stream's close method
+ will be invoked. It can perform any finalization needed for the stream
+ wrapper, but it must not close the underlying stream.
+
+ Note:
+ * Do not use pointers inside Restream
+
+ Parameters:
+ * subcon - the subcon
+ * stream_reader - the read-wrapper
+ * stream_writer - the write wrapper
+ * resizer - a function that takes the size of the subcon and "adjusts"
+ or "resizes" it according to the encoding/decoding process.
+
+ Example:
+ Restream(BitField("foo", 16),
+ stream_reader = BitStreamReader,
+ stream_writer = BitStreamWriter,
+ resizer = lambda size: size / 8,
+ )
+ """
+ __slots__ = ["stream_reader", "stream_writer", "resizer"]
+ def __init__(self, subcon, stream_reader, stream_writer, resizer):
+ Subconstruct.__init__(self, subcon)
+ self.stream_reader = stream_reader
+ self.stream_writer = stream_writer
+ self.resizer = resizer
+ def _parse(self, stream, context):
+ stream2 = self.stream_reader(stream)
+ obj = self.subcon._parse(stream2, context)
+ stream2.close()
+ return obj
+ def _build(self, obj, stream, context):
+ stream2 = self.stream_writer(stream)
+ self.subcon._build(obj, stream2, context)
+ stream2.close()
+ def _sizeof(self, context):
+ return self.resizer(self.subcon._sizeof(context))
+
+
+#===============================================================================
+# miscellaneous
+#===============================================================================
+class Reconfig(Subconstruct):
+ """
+ Reconfigures a subconstruct. Reconfig can be used to change the name and
+ set and clear flags of the inner subcon.
+
+ Parameters:
+ * name - the new name
+ * subcon - the subcon to reconfigure
+ * setflags - the flags to set (default is 0)
+ * clearflags - the flags to clear (default is 0)
+
+ Example:
+ Reconfig("foo", UBInt8("bar"))
+ """
+ __slots__ = []
+ def __init__(self, name, subcon, setflags = 0, clearflags = 0):
+ Construct.__init__(self, name, subcon.conflags)
+ self.subcon = subcon
+ self._set_flag(setflags)
+ self._clear_flag(clearflags)
+
+class Anchor(Construct):
+ """
+ Returns the "anchor" (stream position) at the point where it's inserted.
+ Useful for adjusting relative offsets to absolute positions, or to measure
+ sizes of constructs.
+ absolute pointer = anchor + relative offset
+ size = anchor_after - anchor_before
+ See also Pointer.
+
+ Notes:
+ * requires a seekable stream.
+
+ Parameters:
+ * name - the name of the anchor
+
+ Example:
+ Struct("foo",
+ Anchor("base"),
+ UBInt8("relative_offset"),
+ Pointer(lambda ctx: ctx.relative_offset + ctx.base,
+ UBInt8("data")
+ )
+ )
+ """
+ __slots__ = []
+ def _parse(self, stream, context):
+ return stream.tell()
+ def _build(self, obj, stream, context):
+ context[self.name] = stream.tell()
+ def _sizeof(self, context):
+ return 0
+
+class Value(Construct):
+ """
+ A computed value.
+
+ Parameters:
+ * name - the name of the value
+ * func - a function that takes the context and return the computed value
+
+ Example:
+ Struct("foo",
+ UBInt8("width"),
+ UBInt8("height"),
+ Value("total_pixels", lambda ctx: ctx.width * ctx.height),
+ )
+ """
+ __slots__ = ["func"]
+ def __init__(self, name, func):
+ Construct.__init__(self, name)
+ self.func = func
+ self._set_flag(self.FLAG_DYNAMIC)
+ def _parse(self, stream, context):
+ return self.func(context)
+ def _build(self, obj, stream, context):
+ context[self.name] = self.func(context)
+ def _sizeof(self, context):
+ return 0
+
+#class Dynamic(Construct):
+# """
+# Dynamically creates a construct and uses it for parsing and building.
+# This allows you to create change the construction tree on the fly.
+# Deprecated.
+#
+# Parameters:
+# * name - the name of the construct
+# * factoryfunc - a function that takes the context and returns a new
+# construct object which will be used for parsing and building.
+#
+# Example:
+# def factory(ctx):
+# if ctx.bar == 8:
+# return UBInt8("spam")
+# if ctx.bar == 9:
+# return String("spam", 9)
+#
+# Struct("foo",
+# UBInt8("bar"),
+# Dynamic("spam", factory),
+# )
+# """
+# __slots__ = ["factoryfunc"]
+# def __init__(self, name, factoryfunc):
+# Construct.__init__(self, name, self.FLAG_COPY_CONTEXT)
+# self.factoryfunc = factoryfunc
+# self._set_flag(self.FLAG_DYNAMIC)
+# def _parse(self, stream, context):
+# return self.factoryfunc(context)._parse(stream, context)
+# def _build(self, obj, stream, context):
+# return self.factoryfunc(context)._build(obj, stream, context)
+# def _sizeof(self, context):
+# return self.factoryfunc(context)._sizeof(context)
+
+class LazyBound(Construct):
+ """
+ Lazily bound construct, useful for constructs that need to make cyclic
+ references (linked-lists, expression trees, etc.).
+
+ Parameters:
+
+
+ Example:
+ foo = Struct("foo",
+ UBInt8("bar"),
+ LazyBound("next", lambda: foo),
+ )
+ """
+ __slots__ = ["bindfunc", "bound"]
+ def __init__(self, name, bindfunc):
+ Construct.__init__(self, name)
+ self.bound = None
+ self.bindfunc = bindfunc
+ def _parse(self, stream, context):
+ if self.bound is None:
+ self.bound = self.bindfunc()
+ return self.bound._parse(stream, context)
+ def _build(self, obj, stream, context):
+ if self.bound is None:
+ self.bound = self.bindfunc()
+ self.bound._build(obj, stream, context)
+ def _sizeof(self, context):
+ if self.bound is None:
+ self.bound = self.bindfunc()
+ return self.bound._sizeof(context)
+
+class Pass(Construct):
+ """
+ A do-nothing construct, useful as the default case for Switch, or
+ to indicate Enums.
+ See also Switch and Enum.
+
+ Notes:
+ * this construct is a singleton. do not try to instatiate it, as it
+ will not work...
+
+ Example:
+ Pass
+ """
+ __slots__ = []
+ def _parse(self, stream, context):
+ pass
+ def _build(self, obj, stream, context):
+ assert obj is None
+ def _sizeof(self, context):
+ return 0
+Pass = Pass(None)
+
+class Terminator(Construct):
+ """
+ Asserts the end of the stream has been reached at the point it's placed.
+ You can use this to ensure no more unparsed data follows.
+
+ Notes:
+ * this construct is only meaningful for parsing. for building, it's
+ a no-op.
+ * this construct is a singleton. do not try to instatiate it, as it
+ will not work...
+
+ Example:
+ Terminator
+ """
+ __slots__ = []
+ def _parse(self, stream, context):
+ if stream.read(1):
+ raise TerminatorError("expected end of stream")
+ def _build(self, obj, stream, context):
+ assert obj is None
+ def _sizeof(self, context):
+ return 0
+Terminator = Terminator(None)
View
160 construct/debug.py
@@ -0,0 +1,160 @@
+"""
+Debugging utilities for constructs
+"""
+import sys
+import traceback
+import pdb
+import inspect
+from core import Construct, Subconstruct
+from lib import HexString, Container, ListContainer
+
+
+class Probe(Construct):
+ """
+ A probe: dumps the context, stack frames, and stream content to the screen
+ to aid the debugging process.
+ See also Debugger.
+
+ Parameters:
+ * name - the display name
+ * show_stream - whether or not to show stream contents. default is True.
+ the stream must be seekable.
+ * show_context - whether or not to show the context. default is True.
+ * show_stack - whether or not to show the upper stack frames. default
+ is True.
+ * stream_lookahead - the number of bytes to dump when show_stack is set.
+ default is 100.
+
+ Example:
+ Struct("foo",
+ UBInt8("a"),
+ Probe("between a and b"),
+ UBInt8("b"),
+ )
+ """
+ __slots__ = [
+ "printname", "show_stream", "show_context", "show_stack",
+ "stream_lookahead"
+ ]
+ counter = 0
+
+ def __init__(self, name = None, show_stream = True,
+ show_context = True, show_stack = True,
+ stream_lookahead = 100):
+ Construct.__init__(self, None)
+ if name is None:
+ Probe.counter += 1
+ name = "<unnamed %d>" % (Probe.counter,)
+ self.printname = name
+ self.show_stream = show_stream
+ self.show_context = show_context
+ self.show_stack = show_stack
+ self.stream_lookahead = stream_lookahead
+ def __repr__(self):
+ return "%s(%r)" % (self.__class__.__name__, self.printname)
+ def _parse(self, stream, context):
+ self.printout(stream, context)
+ def _build(self, obj, stream, context):
+ self.printout(stream, context)
+ def _sizeof(self, context):
+ return 0
+
+ def printout(self, stream, context):
+ obj = Container()
+ if self.show_stream:
+ obj.stream_position = stream.tell()
+ follows = stream.read(self.stream_lookahead)
+ if not follows:
+ obj.following_stream_data = "EOF reached"
+ else:
+ stream.seek(-len(follows), 1)
+ obj.following_stream_data = HexString(follows)
+ print
+
+ if self.show_context:
+ obj.context = context
+
+ if self.show_stack:
+ obj.stack = ListContainer()
+ frames = [s[0] for s in inspect.stack()][1:-1]
+ frames.reverse()
+ for f in frames:
+ a = Container()
+ a.__update__(f.f_locals)
+ obj.stack.append(a)
+
+ print "=" * 80
+ print "Probe", self.printname
+ print obj
+ print "=" * 80
+
+class Debugger(Subconstruct):
+ """
+ A pdb-based debugger. When an exception occurs in the subcon, a debugger
+ will appear and allow you to debug the error (and even fix on-the-fly).
+
+ Parameters:
+ * subcon - the subcon to debug
+
+ Example:
+ Debugger(
+ Enum(UBInt8("foo"),
+ a = 1,
+ b = 2,
+ c = 3
+ )
+ )
+ """
+ __slots__ = ["retval"]
+ def _parse(self, stream, context):
+ try:
+ return self.subcon._parse(stream, context)
+ except Exception:
+ self.retval = NotImplemented
+ self.handle_exc("(you can set the value of 'self.retval', "
+ "which will be returned)")
+ if self.retval is NotImplemented:
+ raise
+ else:
+ return self.retval
+ def _build(self, obj, stream, context):
+ try:
+ self.subcon._build(obj, stream, context)
+ except Exception:
+ self.handle_exc()
+ def handle_exc(self, msg = None):
+ print "=" * 80
+ print "Debugging exception of %s:" % (self.subcon,)
+ print "".join(traceback.format_exception(*sys.exc_info())[1:])
+ if msg:
+ print msg
+ pdb.post_mortem(sys.exc_info()[2])
+ print "=" * 80
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
View
0  construct/formats/__init__.py
No changes.
View
3  construct/formats/data/__init__.py
@@ -0,0 +1,3 @@
+"""
+all sorts of raw data serialization (tcpdump capture files, etc.)
+"""
View
55 construct/formats/data/cap.py
@@ -0,0 +1,55 @@
+"""
+tcpdump capture file
+"""
+from construct import *
+import time
+from datetime import datetime
+
+
+class MicrosecAdapter(Adapter):
+ def _decode(self, obj, context):
+ return datetime.fromtimestamp(obj[0] + (obj[1] / 1000000.0))
+ def _encode(self, obj, context):
+ offset = time.mktime(*obj.timetuple())
+ sec = int(offset)
+ usec = (offset - sec) * 1000000
+ return (sec, usec)
+
+packet = Struct("packet",
+ MicrosecAdapter(
+ Sequence("time",
+ ULInt32("time"),
+ ULInt32("usec"),
+ )
+ ),
+ ULInt32("length"),
+ Padding(4),
+ HexDumpAdapter(Field("data", lambda ctx: ctx.length)),
+)
+
+cap_file = Struct("cap_file",
+ Padding(24),
+ Rename("packets", OptionalGreedyRange(packet)),
+)
+
+
+if __name__ == "__main__":
+ obj = cap_file.parse_stream(open("../../test/cap2.cap", "rb"))
+ print len(obj.packets)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
View
0  construct/formats/executable/__init__.py
No changes.
View
156 construct/formats/executable/elf32.py
@@ -0,0 +1,156 @@
+"""
+Executable and Linkable Format (ELF), 32 bit, big or little endian
+Used on *nix systems as a replacement of the older a.out format
+
+Big-endian support kindly submitted by Craig McQueen (mcqueen-c#edsrd1!yzk!co!jp)
+"""
+from construct import *
+
+
+def elf32_body(ElfInt16, ElfInt32):
+ elf32_program_header = Struct("program_header",
+ Enum(ElfInt32("type"),
+ NULL = 0,
+ LOAD = 1,
+ DYNAMIC = 2,
+ INTERP = 3,
+ NOTE = 4,
+ SHLIB = 5,
+ PHDR = 6,
+ _default_ = Pass,
+ ),
+ ElfInt32("offset"),
+ ElfInt32("vaddr"),
+ ElfInt32("paddr"),
+ ElfInt32("file_size"),
+ ElfInt32("mem_size"),
+ ElfInt32("flags"),
+ ElfInt32("align"),
+ )
+
+ elf32_section_header = Struct("section_header",
+ ElfInt32("name_offset"),
+ Pointer(lambda ctx: ctx._.strtab_data_offset + ctx.name_offset,
+ CString("name")
+ ),
+ Enum(ElfInt32("type"),
+ NULL = 0,
+ PROGBITS = 1,
+ SYMTAB = 2,
+ STRTAB = 3,
+ RELA = 4,
+ HASH = 5,
+ DYNAMIC = 6,
+ NOTE = 7,
+ NOBITS = 8,
+ REL = 9,
+ SHLIB = 10,