class Point(FixedObject): classID = 32 _construct = Struct( "", Rename("x", field), Rename("y", field), ) def __init__(self, x, y=None): if y is None: (x, y) = x self.x = x self.y = y @property def value(self): return (self.x, self.y) def __iter__(self): return iter(self.value) def __repr__(self): return 'Point(%r, %r)' % self.value def to_value(self): return Container(x=self.x, y=self.y) @classmethod def from_value(cls, value): return cls(value.x, value.y)
class Dictionary(Collection): classID = 24 _construct = Struct("dictionary", UBInt32("length"), MetaRepeater(lambda ctx: ctx.length, Struct("items", Rename( "key", field), Rename( "value", field), )), ) def __init__(self, value=None): if value == None: value = {} Collection.__init__(self, value) def to_value(self): items = [Container(key=key, value=value) for (key, value) in dict(self.value).items()] return Container(items=items, length=len(items)) @classmethod def from_value(cls, obj): value = dict([(item.key, item.value) for item in obj.items]) return cls(value) def __getattr__(self, name): if name.startswith('__') and name.endswith('__'): return super(Dictionary, self).__getattr__(name) return getattr(self.value, name) def copy(self): return self.__class__(self.value.copy())
class Collection(FixedObjectWithRepeater, ContainsRefs): _construct = Struct( "", UBInt32("length"), MetaRepeater(lambda ctx: ctx.length, Rename("items", field)), ) def __init__(self, value=None): if value == None: value = [] FixedObject.__init__(self, value) def __iter__(self): return iter(self.value) def __getattr__(self, name): if name in ('append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'): return getattr(self.value, name) return super(Collection, self).__getattr__(name) def __getitem__(self, index): return self.value[index] def __setitem__(self, index, value): self.value[index] = value def __delitem__(self, index): del self.value[index] def __len__(self): return len(self.value) def copy(self): return self.__class__(list(self.value.copy))
class ColorForm(Form): """A rectangular array of pixels, used for holding images. width, height - dimensions depth - how many bits are used to specify the color at each pixel. bits - a Bitmap with varying internal structure, depending on depth. colors - the colors pointed to by the bits array. (I think?) privateOffset - ? """ classID = 35 _construct = Struct("", Embed(Form._construct), Rename("colors", field), # Array )
class Form(FixedObject, ContainsRefs): """A rectangular array of pixels, used for holding images. Attributes: width, height - dimensions depth - how many bits are used to specify the color at each pixel. bits - a Bitmap with varying internal structure, depending on depth. privateOffset - ? Note: do not modify the dict returned from the .value property. """ classID = 34 _construct = Struct( "form", Rename("width", field), Rename("height", field), Rename("depth", field), Rename("privateOffset", field), Rename("bits", field), # Bitmap ) def __init__(self, **fields): self.width = 0 self.height = 0 self.depth = None self.privateOffset = None self.bits = Bitmap("") self.colors = None self.__dict__.update(fields) @property def value(self): return dict((k, getattr(self, k)) for k in self.__dict__ if not k.startswith("_")) def to_value(self): return Container(**self.value) @classmethod def from_value(cls, value): return cls(**dict(value)) def copy(self): return self.__class__.from_value(self.to_value()) def __repr__(self): return "<%s(%ix%i)>" % ( self.__class__.__name__, self.width, self.height, ) def built(self): if isinstance(self.bits, ByteArray): self.bits = Bitmap.from_byte_array(self.bits.value) assert isinstance(self.bits, Bitmap) def to_array(self): pixel_bytes = bytearray(self.bits.value) if self.depth == 32: argb_array = pixel_bytes elif self.depth <= 8: wide_argb_array = bytearray(len(pixel_bytes) * 4) num_colors = 2**self.depth if self.colors: colors = [color.to_argb_array() for color in self.colors] colors += [None] * (num_colors - len(colors)) else: colors = default_colormap()[0:num_colors] assert len(colors) == num_colors # Precompute pixel colors for each byte. # This is way faster than doing different bit-shifting ops to get # each pixel depending on depth. multicolors = itertools.product(colors, repeat=8 // self.depth) multicolors = [ bytearray(b'').join(multicolor) for multicolor in multicolors ] wide_argb_array = bytearray(b'').join( operator.itemgetter(*pixel_bytes)(multicolors)) # Rows are rounded to be a whole number of words (32 bits) long. # Presumably this is because Bitmaps are compressed (run-length # encoded) in 32-bit segments. pixels_per_word = 32 // self.depth pixels_in_last_word = self.width % pixels_per_word skip = (pixels_per_word - pixels_in_last_word) % pixels_per_word out_rowlen = self.width * 4 in_rowlen = out_rowlen + skip * 4 row_indexes = xrange(0, len(wide_argb_array), in_rowlen) argb_array = bytearray(b'').join(wide_argb_array[i:i + out_rowlen] for i in row_indexes) else: raise NotImplementedError # TODO: depth 16 size = (self.width, self.height) return PIL.Image.frombuffer("RGBA", size, buffer(argb_array), "raw", "ARGB", 0, 1) @classmethod def from_string(cls, width, height, rgba_string): """Returns a Form with 32-bit RGBA pixels Accepts string containing raw RGBA color values """ # Convert RGBA string to ARGB raw = "" for i in range(0, len(rgba_string), 4): raw += rgba_string[i + 3] # alpha raw += rgba_string[i:i + 3] # rgb assert len(rgba_string) == width * height * 4 return Form( width=width, height=height, depth=32, bits=Bitmap(raw), )
layer3_payload = Switch( "next", lambda ctx: ctx["header"].protocol, {"TCP": layer4_tcp, "UDP": layer4_udp}, default=Pass ) layer3_ipv4 = Struct("layer3_ipv4", Rename("header", ipv4_header), layer3_payload) layer3_ipv6 = Struct("layer3_ipv6", Rename("header", ipv6_header), layer3_payload) layer2_ethernet = Struct( "layer2_ethernet", Rename("header", ethernet_header), Switch("next", lambda ctx: ctx["header"].type, {"IPv4": layer3_ipv4, "IPv6": layer3_ipv6}, default=Pass), ) ip_stack = Rename("ip_stack", layer2_ethernet) if __name__ == "__main__": cap1 = unhexlify( six.b( "0011508c283c001150886b570800450001e971474000800684e4c0a80202525eedda11" "2a0050d98ec61d54fe977d501844705dcc0000474554202f20485454502f312e310d0a" "486f73743a207777772e707974686f6e2e6f72670d0a557365722d4167656e743a204d" "6f7a696c6c612f352e30202857696e646f77733b20553b2057696e646f7773204e5420" "352e313b20656e2d55533b2072763a312e382e302e3129204765636b6f2f3230303630" "3131312046697265666f782f312e352e302e310d0a4163636570743a20746578742f78" "6d6c2c6170706c69636174696f6e2f786d6c2c6170706c69636174696f6e2f7868746d" "6c2b786d6c2c746578742f68746d6c3b713d302e392c746578742f706c61696e3b713d" "302e382c696d6167652f706e672c2a2f2a3b713d302e350d0a4163636570742d4c616e" "67756167653a20656e2d75732c656e3b713d302e350d0a4163636570742d456e636f64"
def greedy_string(name): """ Variable-length string field. """ return Rename(name, StringAdapter(OptionalGreedyRange(Field(None, 1))))
""" TCP/IP Protocol Stack Note: before parsing the application layer over a TCP stream, you must first combine all the TCP frames into a stream. See utils.tcpip for some solutions """ from construct import Struct, Rename, HexDumpAdapter, Field, Switch, Pass from construct.protocols.layer2.ethernet import ethernet_header from construct.protocols.layer3.ipv4 import ipv4_header from construct.protocols.layer3.ipv6 import ipv6_header from construct.protocols.layer4.tcp import tcp_header from construct.protocols.layer4.udp import udp_header layer4_tcp = Struct( "layer4_tcp", Rename("header", tcp_header), HexDumpAdapter( Field( "next", lambda ctx: ctx["_"]["header"].payload_length - ctx[ "header"].header_length)), ) layer4_udp = Struct( "layer4_udp", Rename("header", udp_header), HexDumpAdapter(Field("next", lambda ctx: ctx["header"].payload_length)), ) layer3_payload = Switch("next", lambda ctx: ctx["header"].protocol, { "TCP": layer4_tcp,
CELL_MEASURE_REPORT = Struct("cell_measure_report", UBInt8("type"), UBInt8("version"), Bytes("enbid", 8), UBInt16("cellid"), UBInt32("xid"), BitStruct("flags", Padding(15), Bit("dir")), UBInt32("seq"), UBInt16("length"), UBInt16("action"), UBInt32("interval"), UBInt8("opcode")) CELL_MEASURE_REQUEST = Struct("cell_measure_request", UBInt8("type"), UBInt8("version"), Bytes("enbid", 8), UBInt16("cellid"), UBInt32("xid"), BitStruct("flags", Padding(15), Bit("dir")), UBInt32("seq"), UBInt16("length"), UBInt16("action"), UBInt8("opcode"), UBInt32("interval"), Rename("options", OptionalGreedyRange(OPTIONS))) CELL_MEASURE_RESPONSE = Struct("cell_measure_response", Rename("options", OptionalGreedyRange(OPTIONS))) MAC_PRBS_REQUEST = Struct("mac_prbs_request") MAC_PRBS_REPORT = Struct("mac_prbs_report", UBInt8("dl_prbs_total"), UBInt32("dl_prbs_used"), UBInt8("ul_prbs_total"), UBInt32("ul_prbs_used")) EP_MAC_PRBS_REQUEST = 0x0101 EP_MAC_PRBS_REPORT = 0x0102 CELL_MEASURE_TYPES = {EP_MAC_PRBS_REPORT: MAC_PRBS_REPORT}
TODO tests """ from construct import (ULint8, ULint16, Array, Struct, BitStruct, BitField, Byte, SLInt16, Padding, Rename) u16 = ULint16 s16 = SLInt16 u8 = ULint8 AccelerometerData = Struct("AccelerometerData", s16("x_accel"), s16("y_accel"), s16("z_accel")) Dummy24Bits = BitStruct("s24", BitField("value", 24)) GyroscopeData = Struct("AccelerometerData", Rename("roll", Dummy24Bits), Rename("yaw", Dummy24Bits), Rename("pitch", Dummy24Bits)) MagnetData = Struct("MagnetData", Array(6, Byte("unknown"))) ### Some notes on the Point field usage ## Touchscreen Pressure # Stored as a 12 bit integer in the extra data of the first two points. It is # not yet known how to translate this value to a usable pressure value - # currently it is assumed to be a resistance value reading. ## UIC firmware version # 16 bit integer stored in the extra data of points 6 to 8 (only one bit of the # first coordinate of point 6 is used). Point = Struct( "Point",
Rename("header", ipv6_header), layer3_payload, ) layer2_ethernet = Struct("layer2_ethernet", Rename("header", ethernet_header), Switch("next", lambda ctx: ctx["header"].type, { "IPv4" : layer3_ipv4, "IPv6" : layer3_ipv6, }, default = Pass, ) ) ip_stack = Rename("ip_stack", layer2_ethernet) if __name__ == "__main__": cap1 = ( "0011508c283c001150886b570800450001e971474000800684e4c0a80202525eedda11" "2a0050d98ec61d54fe977d501844705dcc0000474554202f20485454502f312e310d0a" "486f73743a207777772e707974686f6e2e6f72670d0a557365722d4167656e743a204d" "6f7a696c6c612f352e30202857696e646f77733b20553b2057696e646f7773204e5420" "352e313b20656e2d55533b2072763a312e382e302e3129204765636b6f2f3230303630" "3131312046697265666f782f312e352e302e310d0a4163636570743a20746578742f78" "6d6c2c6170706c69636174696f6e2f786d6c2c6170706c69636174696f6e2f7868746d" "6c2b786d6c2c746578742f68746d6c3b713d302e392c746578742f706c61696e3b713d" "302e382c696d6167652f706e672c2a2f2a3b713d302e350d0a4163636570742d4c616e" "67756167653a20656e2d75732c656e3b713d302e350d0a4163636570742d456e636f64" "696e673a20677a69702c6465666c6174650d0a4163636570742d436861727365743a20"
E_SINGLE = Struct("e_single", UBInt16("action"), UBInt8("opcode")) E_TRIG = Struct("e_trig", UBInt16("action"), UBInt8("opcode")) HELLO = Struct("hello", UBInt32("padding")) CAPS_REQUEST = Struct("caps_request", UBInt8("type"), UBInt8("version"), Bytes("enbid", 8), UBInt16("cellid"), UBInt32("xid"), BitStruct("flags", Padding(15), Bit("dir")), UBInt32("seq"), UBInt16("length"), UBInt16("action"), UBInt8("opcode"), UBInt32("dummy")) CAPS_RESPONSE = Struct( "caps_response", BitStruct("flags", Padding(29), Bit("handover"), Bit("ue_measure"), Bit("ue_report")), Rename("options", OptionalGreedyRange(OPTIONS))) RAN_MAC_SLICE_REQUEST = Struct("ran_mac_slice_request", UBInt8("type"), UBInt8("version"), Bytes("enbid", 8), UBInt16("cellid"), UBInt32("xid"), BitStruct("flags", Padding(15), Bit("dir")), UBInt32("seq"), UBInt16("length"), UBInt16("action"), UBInt8("opcode"), Bytes("plmn_id", 4), UBInt8("dscp"), Bytes("padding", 3)) SET_RAN_MAC_SLICE_REQUEST = Struct( "set_ran_mac_slice_request", UBInt8("type"), UBInt8("version"), Bytes("enbid", 8), UBInt16("cellid"), UBInt32("xid"), BitStruct("flags", Padding(15), Bit("dir")), UBInt32("seq"), UBInt16("length"), UBInt16("action"), UBInt8("opcode"),
ASSOC_RESPONSE = Struct("assoc_response", UBInt8("version"), UBInt8("type"), UBInt32("length"), UBInt32("seq"), Bytes("sta", 6)) NETWORKS = Struct("networks", Bytes("bssid", 6), Bytes("ssid", WIFI_NWID_MAXSIZE + 1)) ADD_LVAP = Struct( "add_lvap", UBInt8("version"), UBInt8("type"), UBInt32("length"), UBInt32("seq"), UBInt32("module_id"), BitStruct("flags", Padding(13), Bit("set_mask"), Bit("associated"), Bit("authenticated")), UBInt16("assoc_id"), Bytes("hwaddr", 6), UBInt8("channel"), UBInt8("band"), UBInt8("supported_band"), Bytes("sta", 6), Bytes("encap", 6), Bytes("bssid", 6), Bytes("ssid", WIFI_NWID_MAXSIZE + 1), Rename("networks", OptionalGreedyRange(NETWORKS))) DEL_LVAP = Struct("del_lvap", UBInt8("version"), UBInt8("type"), UBInt32("length"), UBInt32("seq"), UBInt32("module_id"), Bytes("sta", 6), UBInt8("csa_switch_mode"), UBInt8("csa_switch_count"), UBInt8("csa_switch_channel")) STATUS_LVAP = Struct( "status_lvap", UBInt8("version"), UBInt8("type"), UBInt32("length"), UBInt32("seq"), BitStruct("flags", Padding(13), Bit("set_mask"), Bit("associated"), Bit("authenticated")), UBInt16("assoc_id"), Bytes("wtp", 6), Bytes("sta", 6), Bytes("encap", 6), Bytes("hwaddr", 6), UBInt8("channel"), UBInt8("band"), UBInt8("supported_band"), Bytes("bssid", 6), Bytes("ssid", WIFI_NWID_MAXSIZE + 1), Rename("networks", OptionalGreedyRange(NETWORKS)))