Context based types

The context

Every OSER instance is aware of the context it is located in. Context based types can use the context to realize context dependent instances. For example it is possible to create a variable length payload for a network packet or create one instance that can represent multiple different network packets, etc. .

The context can be used using a lambda-expression or a method.

String

A oser.String can be used to encode and decode strings. The length can be either fixed (int), variable (callable) or None for null-terminated strings.

class oser.String(length=None, value=b'', padding=b'\x00')

String builds a string serializer with a fixed or variable length.

Parameters
  • length=None – states the string length. Can be a callable (e.g. lambda) or a scalar. If set to None the result is a null-terminated string. If set to an Integer the result is a fixed length string padded with padding. If set to a callable (lambda or function) the result is a fixed or variable length string padded with padding.

  • value="" – the initial string value.

  • padding="" – that padding pattern used for filling the encoded data if value is shorter than length.

byte_size()

Return the size in bytes.

decode(data, full_data=b'', context_data=b'')

Decode a binary string and return the number of bytes that were decoded.

Parameters
  • data (bytes) – the data buffer that is decoded.

  • full_data (bytes) – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the number of bytes that were decoded.

Return type

int

encode(full_data=b'', context_data=b'')

Return the encoded binary string.

Parameters
  • full_data (bytes) – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the encoded binary string.

Return type

bytes

get()

Return the value.

introspect(stop_at=None)

Return the introspection representation of the object as a string.

root()

return root element

set(value)

Set the value.

Parameters

value – the new value

set_fuzzing_values(values)

Set fuzzing values.

Parameters

values (iterable) – the values used for fuzzing.

set_length(length)

Set the length.

Parameters

length=None – states the string length. Can be a callable (e.g. lambda) or a scalar. If set to None the result is a null-terminated string. If set to an Integer the result is a fixed length string padded with padding. If set to a callable (lambda or function) the result is a fixed or variable length string padded with padding.

size()

Return the size in bytes.

up()

Return the parent element.

Fixed length usage:

>>> from oser import String
>>> from oser import to_hex
>>> instance = String(length=10, value="abcdefghi")
>>> print(instance)
'abcdefghi'

>>> print(instance.introspect())
   -    -  String():
   0 \x61      'a'
   1 \x62      'b'
   2 \x63      'c'
   3 \x64      'd'
   4 \x65      'e'
   5 \x66      'f'
   6 \x67      'g'
   7 \x68      'h'
   8 \x69      'i'
   9 \x00      '\x00'

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8|  9
\x61\x62\x63\x64\x65\x66\x67\x68\x69\x00
>>> bytesDecoded = instance.decode(binary)
>>> print(bytesDecoded)
10
>>> print(instance)
'abcdefghi\x00'

Variable length usage in a oser.ByteStruct:

>>> from oser import ByteStruct
>>> from oser import UBInt16
>>> from oser import String
>>> from oser import to_hex
>>>
>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.length = UBInt16(12)
...         self.string = String(length=lambda self: self.length.get(), value="abcdefghijkl")
...
>>> instance = Data()
>>> print(instance)
Data():
    length: 12 (UBInt16)
    string: 'abcdefghijkl'

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      length: 12 (UBInt16)
   1 \x0c
   -    -      string: String():
   2 \x61          'a'
   3 \x62          'b'
   4 \x63          'c'
   5 \x64          'd'
   6 \x65          'e'
   7 \x66          'f'
   8 \x67          'g'
   9 \x68          'h'
  10 \x69          'i'
  11 \x6a          'j'
  12 \x6b          'k'
  13 \x6c          'l'

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8|  9| 10| 11| 12| 13
\x00\x0C\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C
>>> bytesDecoded = instance.decode("\x00\x1A\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x00")
>>> print(bytesDecoded)
28
>>> print(instance)
Data():
    length: 26 (UBInt16)
    string: 'abcdefghijklmnopqrstuvwxyz'

Null-terminated oser.String:

>>> from oser import String, to_hex
>>>
>>> instance = String(length=None, value=b"abc")
>>> print(instance)
'abc'

>>> print(instance.introspect())
   -    -  String():
   0 \x61      'a'
   1 \x62      'b'
   2 \x63      'c'
   3 \x00      '\x00'

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3
\x61\x62\x63\x00
>>> bytesDecoded = instance.decode(b"abcdefg\x00this text will not be parsed!")
>>> print(bytesDecoded)
8
>>> print(instance)
'abcdefg'

>>> print(instance.size())
8
>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7
\x61\x62\x63\x64\x65\x66\x67\x00

Data

A oser.Data can be used to encode and decode data (strings). The length can be either fixed or variable.

class oser.Data(length, value=b'', padding=b'\x00')

Data builds a data serializer with fixed or variable length. Extends oser.String but length must not be None.

Parameters
  • length – states the data length. Must be a callable (e.g. lambda).

  • value="" – the initial data value.

  • padding=b"" – that padding pattern used for filling the encoded data if value is shorter than length.

byte_size()

Return the size in bytes.

decode(data, full_data=b'', context_data=b'')

Decode a binary string and return the number of bytes that were decoded.

Parameters
  • data (bytes) – the data buffer that is decoded.

  • full_data (bytes) – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the number of bytes that were decoded.

Return type

int

encode(full_data=b'', context_data=b'')

Return the encoded binary string.

Parameters
  • full_data (bytes) – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the encoded binary string.

Return type

bytes

get()

Return the value.

introspect(stop_at=None)

Return the introspection representation of the object as a string.

root()

return root element

set(value)

Set the value.

Parameters

value – the new value

set_fuzzing_values(values)

Set fuzzing values.

Parameters

values (iterable) – the values used for fuzzing.

set_length(length)

Set the length.

Parameters

length=None – states the string length. Can be a callable (e.g. lambda) or a scalar. If set to None the result is a null-terminated string. If set to an Integer the result is a fixed length string padded with padding. If set to a callable (lambda or function) the result is a fixed or variable length string padded with padding.

size()

Return the size in bytes.

up()

Return the parent element.

Array

An oser.Array can be used to repeat parts of an instance. The length can be either fixed or variable.

class oser.Array(length, prototype, args=None, kwargs=None, values=None)

Array builds an array serializer with a fixed or variable length.

Parameters
  • length (callable or integer) – states the array length. Can be a callable (e.g. lambda) or a scalar.

  • prototype (an OSER class) – a class that is the prototype for values

  • args=None (tuple) – a tuple of positional arguments that are passed to prototype when it is instantiated when array size is extended. Note: don’t forget a leading comma when passing one value (1,) ((1) is an integer!).

  • kwargs=None (dict) – a dictionary of named arguments that are passed to prototype when it is instantiated when array size is extended.

  • values=None (list containing instances of prototype) – the initial array values. Can be left empty. If empty the array is automatically resized with instances of prototype(*args, **kwargs).

byte_size()

Return the current size in bytes.

decode(data, full_data=b'', context_data=b'')

Decode a binary string and return the number of bytes that were decoded.

Parameters
  • data (bytes) – the data buffer that is decoded.

  • full_data (bytes) – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the number of bytes that were decoded.

Return type

int

encode(full_data=b'', context_data=b'')

Return the encoded binary string.

Parameters
  • full_data (bytes) – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the encoded binary string.

Return type

bytes

fuzzing_iterator(copy=False)

The fuzzing iterator iterates over all combinations of set fuzzing values (oser.ByteType.set_fuzzing_values()). If no fuzzing values are set the current struct is yielded.

Parameters

copy=False (bool) – if set to True the generated fuzzing values are deep copies of the original. Creating these deep copies is slow. If set to False the original struct is retruned and the generated value must be used immediately since the next generated overwrites the values on the same value.

Yields

the fuzzing combinations.

get()

Return the value.

Returns

the value

introspect(stop_at=None)

Return the introspection representation of the object as a string.

Parameters

stop_at=None (object) – stop introspection at stop_at.

root()

return root element

size()

Return the current size in bytes or bits according to the context.

up()

return parent element

Fixed length usage:

>>> from oser import Array
>>> from oser import to_hex
>>> from oser import UBInt16
>>> instance = Array(length=3, prototype=UBInt16, values=[UBInt16(ii) for ii in range(3)])
>>> print(instance)
Array():
[
    @0: 0 (UBInt16)
    @1: 1 (UBInt16)
    @2: 2 (UBInt16)
]

>>> print(instance.introspect())
   -    -  Array():
   -    -  [
   0 \x00      @0: 0 (UBInt16)
   1 \x00
   2 \x00      @1: 1 (UBInt16)
   3 \x01
   4 \x00      @2: 2 (UBInt16)
   5 \x02
   -    -  ]

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5
\x00\x00\x00\x01\x00\x02
>>> bytesDecoded = instance.decode(binary)
>>> print(bytesDecoded)
6
>>> print(instance)
Array():
[
    @0: 0 (UBInt16)
    @1: 1 (UBInt16)
    @2: 2 (UBInt16)
]

>>> print(instance[10])
IndexError: list index out of range

Variable length usage:

>>> from oser import ByteStruct
>>> from oser import Array
>>> from oser import to_hex
>>> from oser import UBInt16
>>>
>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.length = UBInt16(4)
...         self.data = Array(length=lambda self: self.length.get(),
...                           prototype=UBInt16,
...                           args=(1337,), # when range is extended
...                                         # instantiate prototype with this arguments
...                           values=[UBInt16(ii) for ii in range(2)])
...
>>> instance = Data()
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 0 (UBInt16)
        @1: 1 (UBInt16)
        @2: 1337 (UBInt16)
        @3: 1337 (UBInt16)
    ]

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      length: 4 (UBInt16)
   1 \x04
   -    -      data: Array():
   -    -      [
   2 \x00          @0: 0 (UBInt16)
   3 \x00
   4 \x00          @1: 1 (UBInt16)
   5 \x01
   6 \x05          @2: 1337 (UBInt16)
   7 \x39
   8 \x05          @3: 1337 (UBInt16)
   9 \x39
   -    -      ]

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8|  9
\x00\x04\x00\x00\x00\x01\x05\x39\x05\x39
>>> bytesDecoded = instance.decode(binary)
>>> print(bytesDecoded)
10
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 0 (UBInt16)
        @1: 1 (UBInt16)
        @2: 1337 (UBInt16)
        @3: 1337 (UBInt16)
    ]

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      length: 4 (UBInt16)
   1 \x04
   -    -      data: Array():
   -    -      [
   2 \x00          @0: 0 (UBInt16)
   3 \x00
   4 \x00          @1: 1 (UBInt16)
   5 \x01
   6 \x05          @2: 1337 (UBInt16)
   7 \x39
   8 \x05          @3: 1337 (UBInt16)
   9 \x39
   -    -      ]

>>> print(instance.data[3])
1337 (UBInt16)

>>> instance.decode("\x00\x14\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0A\x00\x0B\x00\x0C\x00\x0D\x00\x0E\x00\x0F\x00\x10\x00\x11\x00\x12\x00\x13")
42
>>> print(instance)
Data():
    length: 20 (UBInt16)
    data: Array():
    [
        @0: 0 (UBInt16)
        @1: 1 (UBInt16)
        @2: 2 (UBInt16)
        @3: 3 (UBInt16)
        @4: 4 (UBInt16)
        @5: 5 (UBInt16)
        @6: 6 (UBInt16)
        @7: 7 (UBInt16)
        @8: 8 (UBInt16)
        @9: 9 (UBInt16)
        @10: 10 (UBInt16)
        @11: 11 (UBInt16)
        @12: 12 (UBInt16)
        @13: 13 (UBInt16)
        @14: 14 (UBInt16)
        @15: 15 (UBInt16)
        @16: 16 (UBInt16)
        @17: 17 (UBInt16)
        @18: 18 (UBInt16)
        @19: 19 (UBInt16)
    ]

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      length: 20 (UBInt16)
   1 \x14
   -    -      data: Array():
   -    -      [
   2 \x00          @0: 0 (UBInt16)
   3 \x00
   4 \x00          @1: 1 (UBInt16)
   5 \x01
   6 \x00          @2: 2 (UBInt16)
   7 \x02
   8 \x00          @3: 3 (UBInt16)
   9 \x03
  10 \x00          @4: 4 (UBInt16)
  11 \x04
  12 \x00          @5: 5 (UBInt16)
  13 \x05
  14 \x00          @6: 6 (UBInt16)
  15 \x06
  16 \x00          @7: 7 (UBInt16)
  17 \x07
  18 \x00          @8: 8 (UBInt16)
  19 \x08
  20 \x00          @9: 9 (UBInt16)
  21 \x09
  22 \x00          @10: 10 (UBInt16)
  23 \x0a
  24 \x00          @11: 11 (UBInt16)
  25 \x0b
  26 \x00          @12: 12 (UBInt16)
  27 \x0c
  28 \x00          @13: 13 (UBInt16)
  29 \x0d
  30 \x00          @14: 14 (UBInt16)
  31 \x0e
  32 \x00          @15: 15 (UBInt16)
  33 \x0f
  34 \x00          @16: 16 (UBInt16)
  35 \x10
  36 \x00          @17: 17 (UBInt16)
  37 \x11
  38 \x00          @18: 18 (UBInt16)
  39 \x12
  40 \x00          @19: 19 (UBInt16)
  41 \x13
   -    -      ]

>>> print(instance.data[15])
15 (UBInt16)

Setting and getting items:

>>> from oser import ByteStruct
>>> from oser import Array
>>> from oser import UBInt16
>>>
>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.length = UBInt16(4)
...         self.data = Array(length=lambda self: self.length.get(),
...                           prototype=UBInt16,
...                           values=[UBInt16(ii) for ii in range(2)])
...
>>> instance = Data()
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 0 (UBInt16)
        @1: 1 (UBInt16)
        @2: 0 (UBInt16)
        @3: 0 (UBInt16)
    ]

>>>
>>> # set a single item
... instance.data[1] = 23
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 0 (UBInt16)
        @1: 23 (UBInt16)
        @2: 0 (UBInt16)
        @3: 0 (UBInt16)
    ]

>>>
>>> # set multiple items (with a slice)
... instance.data[:] = [1, 2, 3, 4]
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 1 (UBInt16)
        @1: 2 (UBInt16)
        @2: 3 (UBInt16)
        @3: 4 (UBInt16)
    ]

>>>
>>> # set every second item
... instance.data[::2] = [23, 24]
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 23 (UBInt16)
        @1: 2 (UBInt16)
        @2: 24 (UBInt16)
        @3: 4 (UBInt16)
    ]

>>>
>>> # get a single item
... print(instance.data[3])
4 (UBInt16)

>>>
>>> # get a slice
... print(instance.data[0:2])
[<oser.typedef.UBInt16 object at 0x7f8d8a86a748>, <oser.typedef.UBInt16 object at 0x7f8d8a86d470>]

If

oser.If is used to build conditional parsers when the condition is True or False. Compared to IfElse If is invisble if the condition is False.

class oser.If(condition, if_true)

If builds a conditional serializer for boolean conditions that is invisible if the condition is False.

It is an alias for IfElse(condition=condition, if_true=if_true, if_false=Nothing())

Parameters
  • condition – the condition which selects the desired values. Can be a callable (e.g. lambda) or a scalar.

  • if_true – is used if condition is True.

byte_size()

Return the current size in bytes.

decode(data, full_data=b'', context_data=b'')

Decode a binary string and return the number of bytes that were decoded.

Parameters
  • data (bytes) – the data buffer that is decoded.

  • full_data (bytes) – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the number of bytes that were decoded.

Return type

int

encode(full_data=b'', context_data=b'')

Return the encoded binary string.

Parameters
  • full_data (bytes) – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the encoded binary string.

Return type

bytes

fuzzing_iterator(copy=False)

The fuzzing iterator iterates over all combinations of set fuzzing values (oser.ByteType.set_fuzzing_values()). If no fuzzing values are set the current struct is yielded.

Parameters

copy=False (bool) – if set to True the generated fuzzing values are deep copies of the original. Creating these deep copies is slow. If set to False the original struct is retruned and the generated value must be used immediately since the next generated overwrites the values on the same value.

Yields

the fuzzing combinations.

get()

Return the current value.

get_current()

Return the currently selected object.

get_false_value()

Return the value for a False condition.

get_true_value()

Return the value for a True condition.

introspect(stop_at=None)

Return the introspection representation of the object as a string.

Parameters

stop_at=None (object) – stop introspection at stop_at.

root()

return root element

set(value)

Set the value.

Parameters

value – the new value

set_false_value(value)

Set the value for a False condition.

Parameters

value – the new value for a False condition.

set_true_value(value)

Set the value for a True condition.

Parameters

value – the new value for a True condition.

size()

Return the current size in bytes or bits according to the context.

up()

return parent element

Usage:

>>> from oser import ByteStruct
>>> from oser import If
>>> from oser import UBInt8, UBInt64
>>> from oser import to_hex
>>>
>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.condition = UBInt8(1)
...         self.data = If(condition=lambda self: self.condition.get() > 0,
...                              if_true=UBInt8(1))
...
>>> instance = Data()
>>> print(instance)
Data():
    condition: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      condition: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1
\x01\x01
>>> bytesDecoded = instance.decode(binary)
>>> print(bytesDecoded)
2
>>> print(instance)
Data():
    condition: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      condition: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> instance.condition.set(0)
>>> print(instance)
Data():
    condition: 0 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      condition: 0 (UBInt8)

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0
\x00

IfElse

oser.IfElse is used to build conditional parsers when the condition is True or False. If there are more possible values oser.Switch can be used.

class oser.IfElse(condition, if_true, if_false)

IfElse builds a conditional serializer for boolean conditions.

Parameters
  • condition – the condition which selects the desired values. Can be a callable (e.g. lambda) or a scalar.

  • if_true – is used if condition is True.

  • if_false – is used if condition is False.

byte_size()

Return the current size in bytes.

decode(data, full_data=b'', context_data=b'')

Decode a binary string and return the number of bytes that were decoded.

Parameters
  • data (bytes) – the data buffer that is decoded.

  • full_data (bytes) – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the number of bytes that were decoded.

Return type

int

encode(full_data=b'', context_data=b'')

Return the encoded binary string.

Parameters
  • full_data (bytes) – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the encoded binary string.

Return type

bytes

fuzzing_iterator(copy=False)

The fuzzing iterator iterates over all combinations of set fuzzing values (oser.ByteType.set_fuzzing_values()). If no fuzzing values are set the current struct is yielded.

Parameters

copy=False (bool) – if set to True the generated fuzzing values are deep copies of the original. Creating these deep copies is slow. If set to False the original struct is retruned and the generated value must be used immediately since the next generated overwrites the values on the same value.

Yields

the fuzzing combinations.

get()

Return the current value.

get_current()

Return the currently selected object.

get_false_value()

Return the value for a False condition.

get_true_value()

Return the value for a True condition.

introspect(stop_at=None)

Return the introspection representation of the object as a string.

Parameters

stop_at=None (object) – stop introspection at stop_at.

root()

return root element

set(value)

Set the value.

Parameters

value – the new value

set_false_value(value)

Set the value for a False condition.

Parameters

value – the new value for a False condition.

set_true_value(value)

Set the value for a True condition.

Parameters

value – the new value for a True condition.

size()

Return the current size in bytes or bits according to the context.

up()

return parent element

Usage:

>>> from oser import ByteStruct
>>> from oser import IfElse
>>> from oser import UBInt8, UBInt64
>>> from oser import to_hex
>>>
>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.condition = UBInt8(1)
...         self.data = IfElse(condition=lambda self: self.condition.get() > 0,
...                              if_true=UBInt8(1),
...                              if_false=UBInt64(0x0123456789abcdef)
...                              )
...
>>> instance = Data()
>>> print(instance)
Data():
    condition: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      condition: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1
\x01\x01
>>> bytesDecoded = instance.decode(binary)
>>> print(bytesDecoded)
2
>>> print(instance)
Data():
    condition: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      condition: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> instance.condition.set(0)
>>> print(instance)
Data():
    condition: 0 (UBInt8)
    data: 81985529216486895 (UBInt64)

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      condition: 0 (UBInt8)
   1 \x01      data: 81985529216486895 (UBInt64)
   2 \x23
   3 \x45
   4 \x67
   5 \x89
   6 \xab
   7 \xcd
   8 \xef

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8
\x00\x01\x23\x45\x67\x89\xAB\xCD\xEF

Switch

oser.Switch is used to build conditional parsers where condition is not only True or False. If the condition can only be True or False oser.IfElse can be used, too.

class oser.Switch(condition, values, default=None)

Switch builds a conditional serializer.

Parameters
  • condition – the key which selects the desired values. Can be a callable (e.g. lambda) or a scalar.

  • values – a dictionary that holds key-value pairs. The key is selected by the value (or return value) of condition and the value is used as the actual serializer.

  • default=None – if not None this value is used as a serializer if condition is not found in values

byte_size()

Return the current size in bytes.

decode(data, full_data=b'', context_data=b'')

Decode a binary string and return the number of bytes that were decoded.

Parameters
  • data (bytes) – the data buffer that is decoded.

  • full_data (bytes) – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the number of bytes that were decoded.

Return type

int

delete_value(key)

Delete the value identified by key.

Parameters

key – the key for the value to be deleted.

encode(full_data=b'', context_data=b'')

Return the encoded binary string.

Parameters
  • full_data (bytes) – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the encoded binary string.

Return type

bytes

fuzzing_iterator(copy=False)

The fuzzing iterator iterates over all combinations of set fuzzing values (oser.ByteType.set_fuzzing_values()). If no fuzzing values are set the current struct is yielded.

Parameters

copy=False (bool) – if set to True the generated fuzzing values are deep copies of the original. Creating these deep copies is slow. If set to False the original struct is retruned and the generated value must be used immediately since the next generated overwrites the values on the same value.

Yields

the fuzzing combinations.

get()

Return the current value.

get_current()

Return the currently selected object.

get_items()

Return the items of the values (like in a dict).

get_keys()

Return the keys of the values (like in a dict).

get_value(key)

Get the value for key.

Parameters

key – the key for the condition.

Returns

the value for the condition key.

get_values()

Return the values of the values (like in a dict).

introspect(stop_at=None)

Return the introspection representation of the object as a string.

Parameters

stop_at=None (object) – stop introspection at stop_at.

root()

return root element

set(value)

Set the current value.

Parameters

value – the new value.

set_value(key, value)

Get the value for key.

Parameters
  • key – the key for the condition.

  • value – the value for the key.

size()

Return the current size in bytes or bits according to the context.

up()

return parent element

update_values(values)

Update the values. Overwrites or adds new keys and values.

Parameters

values (dict) – the dict with the new values.

Usage:

>>> from oser import ByteStruct
>>> from oser import Switch
>>> from oser import UBInt8, UBInt16, UBInt32
>>> from oser import to_hex
>>> from oser import Null
>>>
>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.type = UBInt8(1)
...         self.data = Switch(lambda self: self.type.get(),
...                            values={
...                                1: UBInt8(1),
...                                2: UBInt16(2),
...                                3: UBInt32(3)
...                            },
...                            default=Null())
...
>>> instance = Data()
>>> print(instance)
Data():
    type: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      type: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1
\x01\x01
>>> bytesDecoded = instance.decode(binary)
>>> print(bytesDecoded)
2
>>> print(instance)
Data():
    type: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      type: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> # 3
... instance.type.set(3)
>>> print(instance)
Data():
    type: 3 (UBInt8)
    data: 3 (UBInt32)

>>> print(instance.introspect())
   -    -  Data():
   0 \x03      type: 3 (UBInt8)
   1 \x00      data: 3 (UBInt32)
   2 \x00
   3 \x00
   4 \x03

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4
\x03\x00\x00\x00\x03
>>> # non existent value -> Null()
... instance.type.set(100)
>>> print(instance)
Data():
    type: 100 (UBInt8)
    data: Null (Null)

>>> print(instance.introspect())
   -    -  Data():
   0 \x64      type: 100 (UBInt8)
   -    -      data: Null

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0
\x64

Value

oser.Value evaluates a value that only appears in string or introspection representation but not in the encoded data.

class oser.Value(value)

Value evaluates a value for introspection. It does not appear in the encoded data.

Parameters

value (callable) – a method or a lambda to generate the value.

byte_size()

The size is always 0 because Value does not appear in the encoded data.

decode(data, full_data=b'', context_data=b'')

Decode a binary string and return the number of bytes that were decoded.

Parameters
  • data (bytes) – the data buffer that is decoded.

  • full_data (bytes) – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the number of bytes that were decoded.

Return type

int

encode(full_data=b'', context_data=b'')

Return the encoded binary string.

Parameters
  • full_data (bytes) – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns

the encoded binary string.

Return type

bytes

get()

Return the value.

Returns

the value.

introspect(stop_at=None)

Return the introspection representation of the object as a string.

root()

return root element

set(value)

Set the value.

Parameters

value – the new value

set_fuzzing_values(values)

Set fuzzing values.

Parameters

values (iterable) – the values used for fuzzing.

size()

The size is always 0 because Value does not appear in the encoded data.

up()

Return the parent element.

Usage:

>>> from oser import ByteStruct, ULInt8, ULInt16, to_hex, Value
>>> class Foo(ByteStruct):
...     def __init__(self):
...         super(Foo, self).__init__()
...         self.a = ULInt8(2)
...         self.b = ULInt16(7)
...         self.value = Value(value=lambda self: self.a.get() * self.b.get())
...
>>> foo = Foo()
>>> print(foo)
Foo():
    a: 2 (ULInt8)
    b: 7 (ULInt16)
    value: 14 (Value)

>>> print(foo.introspect())
   -    -  Foo():
   0 \x02      a: 2 (ULInt8)
   1 \x07      b: 7 (ULInt16)
   2 \x00
   -    -      value: 14
>>> binary = foo.encode()
>>> print(to_hex(binary))
   0|  1|  2
\x02\x07\x00