{"action":"created","comment":{"url":"https://api.github.com/repos/cea-sec/miasm/pulls/comments/45850036","id":45850036,"diff_hunk":"@@ -0,0 +1,1259 @@\n+\"\"\"This module provides classes to manipulate C structures backed by a VmMngr\n+object (a miasm VM virtual memory).\n+\n+The main idea is to declare the fields of the structure in the class:\n+\n+    # FIXME: \"I\" => \"u32\"\n+    class MyStruct(MemStruct):\n+        fields = [\n+            # Integer field: just struct.pack fields with one value\n+            (\"num\", Num(\"I\")),\n+            (\"flags\", Num(\"B\")),\n+            # Ptr fields are Num, but they can also be dereferenced\n+            # (self.deref_<field>). Deref can be read and set.\n+            (\"other\", Ptr(\"I\", OtherStruct)),\n+            # Ptr to a variable length String\n+            (\"s\", Ptr(\"I\", MemStr)),\n+            (\"i\", Ptr(\"I\", Num(\"I\"))),\n+        ]\n+\n+And access the fields:\n+\n+    mstruct = MyStruct(jitter.vm, addr)\n+    mstruct.num = 3\n+    assert mstruct.num == 3\n+    mstruct.other = addr2\n+    mstruct.deref_other = OtherStruct(jitter.vm, addr)\n+\n+The `addr` argument can be omited if an allocator is set, in which case the\n+structure will be automatically allocated in memory:\n+\n+    my_heap = miasm2.os_dep.common.heap()\n+    set_allocator(my_heap)\n+\n+Note that some structures (e.g. MemStr or MemArray) do not have a static size\n+and cannot be allocated automatically.\n+\n+\n+As you saw previously, to use this module, you just have to inherit from\n+MemStruct and define a list of (<field_name>, <field_definition>). Availabe\n+MemField classes are:\n+\n+    - Num: for number (float or int) handling\n+    - Struct: abstraction over a simple struct pack/unpack\n+    - Ptr: a pointer to another MemStruct instance\n+    - Inline: include another MemStruct as a field (equivalent to having a\n+      struct field into another struct in C)\n+    - Array: a fixed size array of MemFields (points)\n+    - Union: similar to `union` in C, list of MemFields at the same offset in a\n+      structure; the union has the size of the biggest MemField\n+    - BitField: similar to C bitfields, a list of\n+      [(<field_name), (number_of_bits)]; creates fields that correspond to\n+      certain bits of the field\n+\n+A MemField always has a fixed size in memory.\n+\n+\n+Some special memory structures are already implemented; they all are subclasses\n+of MemStruct with a custom implementation:\n+\n+    - MemSelf: this class is just a special marker to reference a MemStruct\n+      subclass inside itself. Works with Ptr and Array (e.g. Ptr(_, MemSelf)\n+      for a pointer the same type as the class who uses this kind of field)\n+    - MemVoid: empty MemStruct, placeholder to be casted to an implemented\n+      MemStruct subclass\n+    - MemStr: represents a string in memory; the encoding can be passed to the\n+      constructor (null terminated ascii/ansi or null terminated utf16)\n+    - MemArray: an unsized array of MemField; unsized here means that there is\n+      no defined sized for this array, equivalent to a int* or char*-style table\n+      in C. It cannot be allocated automatically, since it has no known size\n+    - MemSizedArray: a sized MemArray, can be automatically allocated in memory\n+      and allows more operations than MemArray\n+    - mem: a function that dynamically generates a MemStruct subclass from a\n+      MemField. This class has only one field named \"value\".\n+\n+A MemStruct do not always have a static size (cls.sizeof()) nor a dynamic size\n+(self.get_size()).\n+\"\"\"\n+\n+import logging\n+import struct\n+\n+log = logging.getLogger(__name__)\n+console_handler = logging.StreamHandler()\n+console_handler.setFormatter(logging.Formatter(\"%(levelname)-5s: %(message)s\"))\n+log.addHandler(console_handler)\n+log.setLevel(logging.WARN)\n+\n+# ALLOCATOR is a function(vm, size) -> allocated_address\n+ALLOCATOR = None\n+\n+# Cache for dynamically generated MemStructs\n+DYN_MEM_STRUCT_CACHE = {}\n+\n+def set_allocator(alloc_func):\n+    \"\"\"Set an allocator for this module; allows to instanciate statically sized\n+    MemStructs (i.e. sizeof() is implemented) without specifying the address\n+    (the object is allocated by @alloc_func in the vm.\n+\n+    Args:\n+        alloc_func: func(VmMngr) -> integer_address\n+    \"\"\"\n+    global ALLOCATOR\n+    ALLOCATOR = alloc_func\n+\n+\n+# Helpers\n+\n+def indent(s, size=4):\n+    \"\"\"Indent a string with @size spaces\"\"\"\n+    return ' '*size + ('\\n' + ' '*size).join(s.split('\\n'))\n+\n+\n+# FIXME: copied from miasm2.os_dep.common and fixed\n+def get_str_ansi(vm, addr, max_char=None):\n+    \"\"\"Get a null terminated ANSI encoded string from a VmMngr.\n+\n+    Args:\n+        vm: VmMngr instance\n+        max_char: max number of characters to get in memory\n+    \"\"\"\n+    l = 0\n+    tmp = addr\n+    while ((max_char is None or l < max_char) and\n+           vm.get_mem(tmp, 1) != \"\\x00\"):\n+        tmp += 1\n+        l += 1\n+    return vm.get_mem(addr, l).decode(\"latin1\")\n+\n+\n+# TODO: get_raw_str_utf16 for length calculus\n+def get_str_utf16(vm, addr, max_char=None):\n+    \"\"\"Get a (double) null terminated utf16 little endian encoded string from\n+    a VmMngr. This encoding is mainly used in Windows.\n+\n+    FIXME: the implementation do not work with codepoints that are encoded on\n+    more than 2 bytes in utf16.\n+\n+    Args:\n+        vm: VmMngr instance\n+        max_char: max number of bytes to get in memory\n+    \"\"\"\n+    l = 0\n+    tmp = addr\n+    # TODO: test if fetching per page rather than 2 byte per 2 byte is worth it?\n+    while ((max_char is None or l < max_char) and\n+           vm.get_mem(tmp, 2) != \"\\x00\\x00\"):\n+        tmp += 2\n+        l += 2\n+    s = vm.get_mem(addr, l)\n+    return s.decode('utf-16le')\n+\n+\n+def set_str_ansi(vm, addr, s):\n+    \"\"\"Encode a string to null terminated ascii/ansi and set it in a VmMngr\n+    memory.\n+\n+    Args:\n+        vm: VmMngr instance\n+        addr: start address to serialize the string to\n+        s: the str to serialize\n+    \"\"\"\n+    vm.set_mem(addr, s + \"\\x00\")\n+\n+\n+def set_str_utf16(vm, addr, s):\n+    \"\"\"Same as set_str_ansi with (double) null terminated utf16 encoding.\"\"\"\n+    s = (s + '\\x00').encode('utf-16le')\n+    vm.set_mem(addr, s)\n+\n+\n+# MemField to MemStruct helper\n+\n+def mem(field):\n+    \"\"\"Generate a MemStruct subclass from a field. The field's value can\n+    be accessed through self.value or self.deref_value if field is a Ptr.\n+    \"\"\"\n+    if field in DYN_MEM_STRUCT_CACHE:\n+        return DYN_MEM_STRUCT_CACHE[field]\n+\n+    fields = [(\"value\", field)]\n+    # Build a type to contain the field type\n+    mem_type = type(\"Mem%r\" % field, (MemStruct,), {'fields': fields})\n+    DYN_MEM_STRUCT_CACHE[field] = mem_type\n+    return mem_type\n+\n+\n+# MemField classes\n+\n+class MemField(object):\n+    \"\"\"Base class to provide methods to set and get fields from virtual mem.\n+\n+    Subclasses can either override _pack and _unpack, or get and set if data\n+    serialization requires more work (see Inline implementation for an example).\n+    \"\"\"\n+\n+    _self_type = None\n+\n+    def _pack(self, val):\n+        \"\"\"Serializes the python value @val to a raw str\"\"\"\n+        raise NotImplementedError()\n+\n+    def _unpack(self, raw_str):\n+        \"\"\"Deserializes a raw str to an object representing the python value\n+        of this field.\n+        \"\"\"\n+        raise NotImplementedError()\n+\n+    def set(self, vm, addr, val):\n+        \"\"\"Set a VmMngr memory from a value.\n+\n+        Args:\n+            vm: VmMngr instance\n+            addr: the start adress in memory to set\n+            val: the python value to serialize in @vm at @addr\n+        \"\"\"\n+        raw = self._pack(val)\n+        vm.set_mem(addr, raw)\n+\n+    def get(self, vm, addr):\n+        \"\"\"Get the python value of a field from a VmMngr memory at @addr.\"\"\"\n+        raw = vm.get_mem(addr, self.size())\n+        return self._unpack(raw)\n+\n+    def _get_self_type(self):\n+        return self._self_type\n+\n+    def _set_self_type(self, self_type):\n+        \"\"\"If this field refers to MemSelf, replace it with @self_type (a\n+        MemStruct subclass) when using it. Generally not used outside the lib.\n+        \"\"\"\n+        self._self_type = self_type\n+\n+    def size(self):\n+        \"\"\"Return the size in bytes of the serialized version of this field\"\"\"\n+        raise NotImplementedError()\n+\n+    def __len__(self):\n+        return self.size()\n+\n+    def __neq__(self, other):\n+        return not self == other\n+\n+\n+class Struct(MemField):\n+    \"\"\"Dumb struct.pack/unpack field. Mainly used to factorize code.\n+\n+    Value is a tuple corresponding to the struct @fmt passed to the constructor.\n+    \"\"\"\n+\n+    def __init__(self, fmt):\n+        self._fmt = fmt\n+\n+    def _pack(self, fields):\n+        return struct.pack(self._fmt, *fields)\n+\n+    def _unpack(self, raw_str):\n+        return struct.unpack(self._fmt, raw_str)\n+\n+    def size(self):\n+        return struct.calcsize(self._fmt)\n+\n+    def __repr__(self):\n+        return \"%s(%s)\" % (self.__class__.__name__, self._fmt)\n+\n+    def __eq__(self, other):\n+        return self.__class__ == other.__class__ and self._fmt == other._fmt\n+\n+    def __hash__(self):\n+        return hash(hash(self.__class__) + hash(self._fmt))\n+\n+\n+class Num(Struct):\n+    \"\"\"Represents a number (integer or float). The number is encoded with\n+    a struct-style format which must represent only one value.\n+\n+    TODO: use u32, i16, etc. for format.\n+    \"\"\"\n+\n+    def _pack(self, number):\n+        return super(Num, self)._pack([number])\n+\n+    def _unpack(self, raw_str):\n+        upck = super(Num, self)._unpack(raw_str)\n+        if len(upck) > 1:\n+            raise ValueError(\"Num format string unpacks to multiple values, \"\n+                             \"should be 1\")\n+        return upck[0]\n+\n+\n+class Ptr(Num):\n+    \"\"\"Special case of number of which value indicates the address of a\n+    MemStruct. Provides deref_<field> as well as <field> when used, to set and\n+    get the pointed MemStruct.\n+    \"\"\"\n+\n+    def __init__(self, fmt, dst_type, *type_args, **type_kwargs):\n+        \"\"\"Args:\n+            fmt: (str) Num compatible format that will be the Ptr representation\n+                in memory\n+            dst_type: (MemStruct or MemField) the MemStruct this Ptr points to.\n+                If a MemField is given, it is transformed into a MemStruct with\n+                mem(TheMemField).\n+            *type_args, **type_kwargs: arguments to pass to the the pointed\n+                MemStruct when instanciating it (e.g. for MemStr encoding or\n+                MemArray field_type).\n+        \"\"\"\n+        if not isinstance(dst_type, MemField) and\\\n+                not (isinstance(dst_type, type) and\\\n+                        issubclass(dst_type, MemStruct)) and\\\n+                not dst_type == MemSelf:\n+            raise ValueError(\"dst_type of Ptr must be a MemStruct type, a \"\n+                             \"MemField instance, the MemSelf marker or a class \"\n+                             \"name.\")\n+        super(Ptr, self).__init__(fmt)\n+        if isinstance(dst_type, MemField):\n+            # Patch the field to propagate the MemSelf replacement\n+            dst_type._get_self_type = lambda: self._get_self_type()\n+            dst_type = mem(dst_type)\n+        self._dst_type = dst_type\n+        self._type_args = type_args\n+        self._type_kwargs = type_kwargs\n+\n+    def _fix_dst_type(self):\n+        if self._dst_type == MemSelf:\n+            if self._get_self_type() is not None:\n+                self._dst_type = self._get_self_type()\n+            else:\n+                raise ValueError(\"Unsupported usecase for MemSelf, sorry\")\n+\n+    @property\n+    def dst_type(self):\n+        \"\"\"Return the type (MemStruct subtype) this Ptr points to.\"\"\"\n+        self._fix_dst_type()\n+        return self._dst_type\n+\n+    def deref_get(self, vm, addr):\n+        \"\"\"Deserializes the data in @vm (VmMngr) at @addr to self.dst_type.\n+        Equivalent to a pointer dereference rvalue in C.\n+        \"\"\"\n+        return self.dst_type(vm, addr, *self._type_args, **self._type_kwargs)\n+\n+    def deref_set(self, vm, addr, val):\n+        \"\"\"Serializes the @val MemStruct subclass instance in @vm (VmMngr) at\n+        @addr. Equivalent to a pointer dereference assignment in C.\n+        \"\"\"\n+        # Sanity check\n+        if self.dst_type != val.__class__:\n+            log.warning(\"Original type was %s, overriden by value of type %s\",\n+                        self._dst_type.__name__, val.__class__.__name__)\n+\n+        # Actual job\n+        vm.set_mem(addr, str(val))\n+\n+    def __repr__(self):\n+        return \"%s(%r)\" % (self.__class__.__name__, self._dst_type)\n+\n+    def __eq__(self, other):\n+        return super(Ptr, self).__eq__(other) and \\\n+                self.dst_type == other.dst_type and \\\n+                self._type_args == other._type_args and \\\n+                self._type_kwargs == other._type_kwargs\n+\n+    def __hash__(self):\n+        return hash(super(Ptr, self).__hash__() + hash(self._dst_type) +\n+                hash(self._type_args))\n+\n+\n+class Inline(MemField):\n+    \"\"\"Field used to inline a MemStruct in another MemStruct. Equivalent to\n+    having a struct field in a C struct.\n+\n+    Concretely:\n+\n+        class MyStructClass(MemStruct):\n+            fields = [(\"f1\", Num(\"I\")), (\"f2\", Num(\"I\"))]\n+\n+        class Example(MemStruct):\n+            fields = [(\"mystruct\", Inline(MyStructClass))]\n+\n+        ex = Example(vm, addr)\n+        ex.mystruct.f2 = 3 # inlined structure field access\n+        ex.mystruct = MyStructClass(vm, addr2) # struct copy\n+\n+    It can be seen like a bridge to use a MemStruct as a MemField\n+\n+    TODO: make the Inline implicit when setting a field to be a MemStruct\n+    \"\"\"\n+\n+    def __init__(self, inlined_type, *type_args, **type_kwargs):\n+        if not issubclass(inlined_type, MemStruct):\n+            raise ValueError(\"inlined type if Inline must be a MemStruct\")\n+        self._il_type = inlined_type\n+        self._type_args = type_args\n+        self._type_kwargs = type_kwargs\n+\n+    def set(self, vm, addr, val):\n+        raw = str(val)\n+        vm.set_mem(addr, raw)\n+\n+    def get(self, vm, addr):\n+        return self._il_type(vm, addr)\n+\n+    def size(self):\n+        return self._il_type.sizeof()\n+\n+    def __repr__(self):\n+        return \"%s(%r)\" % (self.__class__.__name__, self._il_type)\n+\n+    def __eq__(self, other):\n+        return self.__class__ == other.__class__ and \\\n+                self._il_type == other._il_type and \\\n+                self._type_args == other._type_args and \\\n+                self._type_kwargs == other._type_kwargs\n+\n+    def __hash__(self):\n+        return hash(hash(self.__class__) + hash(self._il_type) +\n+                hash(self._type_args))\n+\n+\n+class Array(MemField):\n+    \"\"\"A fixed size array (contiguous sequence) of a MemField subclass\n+    elements. Similar to something like the char[10] type in C.\n+\n+    Getting an array field actually returns a MemSizedArray. Setting it is\n+    possible with either a list or a MemSizedArray instance. Examples of syntax:\n+\n+        class Example(MemStruct):\n+            fields = [(\"array\", Array(Num(\"B\"), 4))]\n+\n+        mystruct = Example(vm, addr)\n+        mystruct.array[3] = 27\n+        mystruct.array = [1, 4, 8, 9]\n+        mystruct.array = MemSizedArray(vm, addr2, Num(\"B\"), 4)\n+    \"\"\"\n+\n+    def __init__(self, field_type, array_len):\n+        self.field_type = field_type\n+        self.array_len = array_len\n+\n+    def _set_self_type(self, self_type):\n+        super(Array, self)._set_self_type(self_type)\n+        self.field_type._set_self_type(self_type)\n+\n+    def set(self, vm, addr, val):\n+        # MemSizedArray assignment\n+        if isinstance(val, MemSizedArray):\n+            if val.array_len != self.array_len or len(val) != self.size():\n+                raise ValueError(\"Size mismatch in MemSizedArray assignment\")\n+            raw = str(val)\n+            vm.set_mem(addr, raw)\n+\n+        # list assignment\n+        elif isinstance(val, list):\n+            if len(val) != self.array_len:\n+                raise ValueError(\"Size mismatch in MemSizedArray assignment \")\n+            offset = 0\n+            for elt in val:\n+                self.field_type.set(vm, addr + offset, elt)\n+                offset += self.field_type.size()\n+\n+        else:\n+            raise NotImplementedError(\n+                \"Assignment only implemented for list and MemSizedArray\")\n+\n+    def get(self, vm, addr):\n+        return MemSizedArray(vm, addr, self.field_type, self.array_len)\n+\n+    def size(self):\n+        return self.field_type.size() * self.array_len\n+\n+    def __repr__(self):\n+        return \"%r[%s]\" % (self.field_type, self.array_len)\n+\n+    def __eq__(self, other):\n+        return self.__class__ == other.__class__ and \\\n+                self.field_type == other.field_type and \\\n+                self.array_len == other.array_len\n+\n+    def __hash__(self):\n+        return hash(hash(self.__class__) + hash(self.field_type) +\n+                hash(self.array_len))\n+\n+\n+class Union(MemField):\n+    \"\"\"Allows to put multiple fields at the same offset in a MemStruct, similar\n+    to unions in C. The Union will have the size of the largest of its fields.\n+\n+    Example:\n+\n+        class Example(MemStruct):\n+            fields = [(\"uni\", Union([\n+                                  (\"f1\", Num(\"<B\")),\n+                                  (\"f2\", Num(\"<H\"))\n+                              ])\n+                     )]\n+\n+        ex = Example(vm, addr)\n+        ex.f2 = 0x1234\n+        assert ex.f1 == 0x34\n+        assert ex.uni == '\\x34\\x12'\n+        assert ex.get_addr(\"f1\") == ex.get_addr(\"f2\")\n+    \"\"\"\n+\n+    def __init__(self, field_list):\n+        \"\"\"field_list is a [(name, field)] list, see the class doc\"\"\"\n+        self.field_list = field_list\n+\n+    def size(self):\n+        return max(field.size() for _, field in self.field_list)\n+\n+    def set(self, vm, addr, val):\n+        if not isinstance(val, str) or not len(str) == self.size():\n+            raise ValueError(\"Union can only be set with raw str of the Union's\"\n+                             \" size\")\n+        vm.set_mem(vm, addr, val)\n+\n+    def get(self, vm, addr):\n+        return vm.get_mem(addr, self.size())\n+\n+    def __repr__(self):\n+        fields_repr = ', '.join(\"%s: %r\" % (name, field)\n+                                for name, field in self.field_list)\n+        return \"%s(%s)\" % (self.__class__.__name__, fields_repr)\n+\n+    def __eq__(self, other):\n+        return self.__class__ == other.__class__ and \\\n+                self.field_list == other.field_list\n+\n+    def __hash__(self):\n+        return hash(hash(self.__class__) + hash(tuple(self.field_list)))\n+\n+\n+class Bits(MemField):\n+    \"\"\"Helper class for BitField, not very useful on its own. Represents some\n+    bits of a Num.\n+\n+    The @backing_num is used to know how to serialize/deserialize data in vm,\n+    but getting/setting this fields only affects bits from @bit_offset to\n+    @bit_offset + @bits. Masking and shifting is handled by the class, the aim\n+    is to provide a transparent way to set and get some bits of a num.\n+    \"\"\"\n+\n+    def __init__(self, backing_num, bits, bit_offset):\n+        if not isinstance(backing_num, Num):\n+            raise ValueError(\"backing_num should be a Num instance\")\n+        self._num = backing_num\n+        self._bits = bits\n+        self._bit_offset = bit_offset\n+\n+    def set(self, vm, addr, val):\n+        val_mask = (1 << self._bits) - 1\n+        val_shifted = (val & val_mask) << self._bit_offset\n+        num_size = self._num.size() * 8\n+\n+        full_num_mask = (1 << num_size) - 1\n+        num_mask = (~(val_mask << self._bit_offset)) & full_num_mask\n+\n+        num_val = self._num.get(vm, addr)\n+        res_val = (num_val & num_mask) | val_shifted\n+        self._num.set(vm, addr, res_val)\n+\n+    def get(self, vm, addr):\n+        val_mask = (1 << self._bits) - 1\n+        num_val = self._num.get(vm, addr)\n+        res_val = (num_val >> self._bit_offset) & val_mask\n+        return res_val\n+\n+    def size(self):\n+        return self._num.size()\n+\n+    @property\n+    def bit_size(self):\n+        \"\"\"Number of bits read/written by this class\"\"\"\n+        return self._bits\n+\n+    @property\n+    def bit_offset(self):\n+        \"\"\"Offset in bits (beginning at 0, the LSB) from which to read/write\n+        bits.\n+        \"\"\"\n+        return self._bit_offset\n+\n+    def __repr__(self):\n+        return \"%s%r(%d:%d)\" % (self.__class__.__name__, self._num,\n+                                self._bit_offset, self._bit_offset + self._bits)\n+\n+    def __eq__(self, other):\n+        return self.__class__ == other.__class__ and \\\n+                self._num == other._num and self._bits == other._bits and \\\n+                self._bit_offset == other._bit_offset\n+\n+    def __hash__(self):\n+        return hash(hash(self.__class__) + hash(self._num) + hash(self._bits) +\n+                hash(self._bit_offset))\n+\n+\n+class BitField(Union):\n+    \"\"\"A C-like bitfield.\n+\n+    Constructed with a list [(<field_name>, <number_of_bits>)] and a\n+    @backing_num. The @backing_num is a Num instance that determines the total\n+    size of the bitfield and the way the bits are serialized/deserialized (big\n+    endian int, little endian short...). Can be seen (and implemented) as a\n+    Union of Bits fields.\n+\n+    Creates fields that allow to access the bitfield fields easily. Example:\n+\n+        class Example(MemStruct):\n+            fields = [(\"bf\", BitField(Num(\"B\"), [\n+                                (\"f1\", 2),\n+                                (\"f2\", 4),\n+                                (\"f3\", 1)\n+                             ])\n+                     )]\n+\n+        ex = Example(vm, addr)\n+        ex.memset()\n+        ex.f2 = 2\n+        ex.f1 = 5 # 5 does not fit on two bits, it will be binarily truncated\n+        assert ex.f1 == 3\n+        assert ex.f2 == 2\n+        assert ex.f3 == 0 # previously memset()\n+        assert ex.bf == 3 + 2 << 2\n+    \"\"\"\n+\n+    def __init__(self, backing_num, bit_list):\n+        \"\"\"@backing num: Num intance, @bit_list: [(name, n_bits)]\"\"\"\n+        self._num = backing_num\n+        fields = []\n+        offset = 0\n+        for name, bits in bit_list:\n+            fields.append((name, Bits(self._num, bits, offset)))\n+            offset += bits\n+        if offset > self._num.size() * 8:\n+            raise ValueError(\"sum of bit lengths is > to the backing num size\")\n+        super(BitField, self).__init__(fields)\n+\n+    def set(self, vm, addr, val):\n+        self._num.set(vm, addr, val)\n+\n+    def get(self, vm, addr):\n+        return self._num.get(vm, addr)\n+\n+    def __eq__(self, other):\n+        return self.__class__ == other.__class__ and \\\n+                self._num == other._num and super(BitField, self).__eq__(other)\n+\n+    def __hash__(self):\n+        return hash(super(BitField, self).__hash__() + hash(self._num))\n+\n+\n+# MemStruct classes\n+\n+class _MetaMemStruct(type):\n+    \"\"\"MemStruct metaclass. Triggers the magic that generates the class fields\n+    from the cls.fields list.\n+\n+    Just calls MemStruct.gen_fields(), the actual implementation can seen be\n+    there.\n+    \"\"\"\n+\n+    def __init__(cls, name, bases, dct):\n+        super(_MetaMemStruct, cls).__init__(name, bases, dct)\n+        cls.gen_fields()\n+\n+    def __repr__(cls):\n+        return cls.__name__\n+\n+\n+class MemStruct(object):\n+    \"\"\"Base class to implement VmMngr backed C-like structures in miasm.\n+\n+    The mechanism is the following:\n+        - set a \"fields\" class field to be a list of\n+          (<field_name (str)>, <MemField_subclass_instance>)\n+        - instances of this class will have properties to interract with these\n+          fields.\n+\n+    Example:\n+        class Example(MemStruct):\n+            fields = [\n+                # Number field: just struct.pack fields with one value\n+                (\"num\", Num(\"I\")),\n+                (\"flags\", Num(\"B\")),\n+                # Ptr fields are Num, but they can also be dereferenced\n+                # (self.deref_<field>). Deref can be read and set.\n+                (\"other\", Ptr(\"I\", OtherStruct)),\n+                (\"i\", Ptr(\"I\", Num(\"I\"))),\n+                # Ptr to a variable length String\n+                (\"s\", Ptr(\"I\", MemStr)),\n+            ]\n+\n+        mstruct = MyStruct(vm, addr)\n+\n+        # Field assignment modifies virtual memory\n+        mstruct.num = 3\n+        assert mstruct.num == 3\n+        memval = struct.unpack(\"I\", vm.get_mem(mstruct.get_addr(),\n+                                                      4))[0]\n+        assert memval == mstruct.num\n+\n+        # Memset sets the whole structure\n+        mstruct.memset()\n+        assert mstruct.num == 0\n+        mstruct.memset('\\x11')\n+        assert mstruct.num == 0x11111111\n+\n+        other = OtherStruct(vm, addr2)\n+        mstruct.other = other.get_addr()\n+        assert mstruct.other == other.get_addr()\n+        assert mstruct.deref_other == other\n+        assert mstruct.deref_other.foo == 0x1234\n+\n+    See the various MemField doc for more information.\n+    \"\"\"\n+    __metaclass__ = _MetaMemStruct\n+\n+    fields = []\n+\n+    _size = None\n+\n+    # Classic usage methods\n+\n+    def __init__(self, vm, addr=None, *args, **kwargs):\n+        global ALLOCATOR\n+        super(MemStruct, self).__init__(*args, **kwargs)\n+        self._vm = vm\n+        if addr is None:\n+            if ALLOCATOR is None:\n+                raise ValueError(\"Cannot provide None address to MemStruct() if\"\n+                                 \"%s.set_allocator has not been called.\"\n+                                 % __name__)\n+            self._addr = ALLOCATOR(vm, self.get_size())\n+        else:\n+            self._addr = addr\n+\n+    def get_addr(self, field_name=None):\n+        \"\"\"Return the address of this MemStruct or one of its fields.\n+\n+        Args:\n+            field_name: (str, optional) the name of the field to get the\n+                address of\n+        \"\"\"\n+        if field_name is not None:\n+            if field_name not in self._attrs:\n+                raise ValueError(\"This structure has no %s field\" % field_name)\n+            offset = self._attrs[field_name]['offset']\n+        else:\n+            offset = 0\n+        return self._addr + offset\n+\n+    @classmethod\n+    def sizeof(cls):\n+        \"\"\"Return the static size of this structure, when available (it is the\n+        case by default).\n+        \"\"\"\n+        # Child classes can set cls._size if their size is not the sum of\n+        # their fields\n+        if cls._size is None:\n+            return sum(a[\"field\"].size() for a in cls._attrs.itervalues())\n+        return cls._size\n+\n+    def get_size(self):\n+        \"\"\"Return the dynamic size of this structure (e.g. the size of an\n+        instance). Defaults to sizeof for this base class.\n+\n+        For example, MemSizedArray defines get_size but not sizeof, as an\n+        instance has a fixed size (because it has a fixed length and\n+        field_type), but all the instance do not have the same size.\n+        \"\"\"\n+        return self.sizeof()\n+\n+    def get_field_type(self, name):\n+        \"\"\"Return the MemField subclass instance describing field @name.\"\"\"\n+        return self._attrs[name]['field']\n+\n+    def get_field(self, name):\n+        \"\"\"Get a field value by name.\n+\n+        Useless most of the time since fields are accessible via self.<name>.\n+        \"\"\"\n+        if name not in self._attrs:\n+            raise AttributeError(\"'%s' object has no attribute '%s'\"\n+                                 % (self.__class__.__name__, name))\n+        field = self._attrs[name][\"field\"]\n+        offset = self._attrs[name][\"offset\"]\n+        return field.get(self._vm, self.get_addr() + offset)\n+\n+    def set_field(self, name, val):\n+        \"\"\"Set a field value by name. @val is the python value corresponding to\n+        this field type.\n+\n+        Useless most of the time since fields are accessible via self.<name>.\n+        \"\"\"\n+        if name not in self._attrs:\n+            raise AttributeError(\"'%s' object has no attribute '%s'\"\n+                                 % (self.__class__.__name__, name))\n+        field = self._attrs[name][\"field\"]\n+        offset = self._attrs[name][\"offset\"]\n+        field.set(self._vm, self.get_addr() + offset, val)\n+\n+    def deref_field(self, name):\n+        \"\"\"Get the MemStruct pointed by <name> field.\n+\n+        Useless most of the time since fields are accessible via\n+        self.deref_<name>.\n+        \"\"\"\n+        addr = self.get_field(name)\n+        field = self._attrs[name][\"field\"]\n+        assert isinstance(field, Ptr),\\\n+               \"Programming error: field should be a Ptr\"\n+        return field.deref_get(self._vm, addr)\n+\n+    def set_deref_field(self, name, val):\n+        \"\"\"Set the MemStruct pointed by <name> field. @val should be of the\n+        type of the pointed MemStruct. The field must be a Ptr.\n+\n+        Useless most of the time since fields are accessible via\n+        self.deref_<name>.\n+        \"\"\"\n+        addr = self.get_field(name)\n+        field = self._attrs[name][\"field\"]\n+        assert isinstance(field, Ptr),\\\n+               \"Programming error: field should be a Ptr\"\n+        field.deref_set(self._vm, addr, val)\n+\n+    def memset(self, byte='\\x00'):\n+        \"\"\"Fill the memory space of this MemStruct with @byte ('\\x00' by\n+        default). The size is retrieved with self.get_size() (dynamic size).\n+        \"\"\"\n+        if not isinstance(byte, str) or not len(byte) == 1:\n+            raise ValueError(\"byte must be a 1-lengthed str\")\n+        self._vm.set_mem(self.get_addr(), byte * self.get_size())\n+\n+    def cast(self, other_type, *type_args, **type_kwargs):\n+        \"\"\"Cast this MemStruct to another MemStruct (same address, same vm, but\n+        different type). Return the casted MemStruct.\n+        \"\"\"\n+        return self.cast_field(None, other_type, *type_args, **type_kwargs)\n+\n+    def cast_field(self, field_name, other_type, *type_args, **type_kwargs):\n+        \"\"\"Same as cast, but the address of the returned MemStruct is the\n+        address at which @field_name is in the current MemStruct.\n+        \"\"\"\n+        return other_type(self._vm, self.get_addr(field_name),\n+                          *type_args, **type_kwargs)\n+\n+    def __len__(self):\n+        return self.get_size()\n+\n+    def raw(self):\n+        \"\"\"Raw binary (str) representation of the MemStruct as it is in\n+        memory.\n+        \"\"\"\n+        attrs = sorted(self._attrs.itervalues(), key=lambda a: a[\"offset\"])\n+        out = []\n+        for attr in attrs:\n+            field = attr[\"field\"]\n+            offset = attr[\"offset\"]\n+            out.append(self._vm.get_mem(self.get_addr() + offset, field.size()))\n+        return ''.join(out)\n+\n+    def __str__(self):\n+        return self.raw()\n+\n+    def __repr__(self):\n+        attrs = sorted(self._attrs.iteritems(), key=lambda a: a[1][\"offset\"])\n+        out = []\n+        for name, attr in attrs:\n+            field = attr[\"field\"]\n+            val_repr = repr(self.get_field(name))\n+            if '\\n' in val_repr:\n+                val_repr = '\\n' + indent(val_repr, 4)\n+            out.append(\"%s: %r = %s\" % (name, field, val_repr))\n+        return '%r:\\n' % self.__class__ + indent('\\n'.join(out), 2)\n+\n+    def __eq__(self, other):\n+        return self.__class__ == other.__class__ and str(self) == str(other)\n+\n+    def __ne__(self, other):\n+        return not self == other\n+\n+    # Field generation methods, voluntarily public to be able to regen fields\n+    # after class definition\n+\n+    @classmethod\n+    def gen_fields(cls, fields=None):\n+        \"\"\"Generate the fields of this class (so that they can be accessed with\n+        self.<field_name>) from a @fields list, as described in the class doc.\n+\n+        Useful in case of a type cyclic dependency. For example, the following\n+        is not possible in python:\n+\n+            class A(MemStruct):\n+                fields = [(\"b\", Ptr(\"I\", B))]\n+\n+            class B(MemStruct):\n+                fields = [(\"a\", Ptr(\"I\", A))]\n+\n+        With gen_fields, the following is the legal equivalent:\n+\n+            class A(MemStruct):\n+                pass\n+\n+            class B(MemStruct):\n+                fields = [(\"a\", Ptr(\"I\", A))]\n+\n+            A.fields = [(\"b\", Ptr(\"I\", B))]\n+            a.gen_field()\n+        \"\"\"\n+        if fields is None:\n+            fields = cls.fields\n+        cls._attrs = {}\n+        offset = 0\n+        for name, field in cls.fields:\n+            # For reflexion\n+            field._set_self_type(cls)\n+            cls.gen_field(name, field, offset)\n+            offset += field.size()\n+        cls._size = offset\n+\n+    @classmethod\n+    def gen_field(cls, name, field, offset):\n+        \"\"\"Generate only one field\n+\n+        Args:\n+            @name: (str) the name of the field\n+            @field: (MemField instance) the field type\n+            @offset: (int) the offset of the field in the structure\n+        \"\"\"\n+        cls._gen_simple_attr(name, field, offset)\n+        if isinstance(field, Union):\n+            cls._gen_union_attr(field, offset)\n+\n+    @classmethod\n+    def _gen_simple_attr(cls, name, field, offset):\n+        cls._attrs[name] = {\"field\": field, \"offset\": offset}\n+\n+        # Generate self.<name> getter and setter\n+        setattr(cls, name, property(\n+            lambda self: self.get_field(name),\n+            lambda self, val: self.set_field(name, val)\n+        ))\n+\n+        # Generate self.deref_<name> getter and setter if this field is a\n+        # Ptr\n+        if isinstance(field, Ptr):\n+            setattr(cls, \"deref_%s\" % name, property(\n+                lambda self: self.deref_field(name),\n+                lambda self, val: self.set_deref_field(name, val)\n+            ))\n+\n+    @classmethod\n+    def _gen_union_attr(cls, union_field, offset):\n+        if not isinstance(union_field, Union):\n+            raise ValueError(\"field should be an Union instance\")\n+        for name, field in union_field.field_list:\n+            cls.gen_field(name, field, offset)\n+\n+\n+class MemSelf(MemStruct):\n+    \"\"\"Special Marker class for reference to current class in a Ptr or Array\n+    (mostly Array of Ptr).\n+\n+    Example:\n+        class ListNode(MemStruct):\n+            fields = [\n+                (\"next\", Ptr(\"<I\", MemSelf)),\n+                (\"data\", Ptr(\"<I\", MemVoid)),\n+            ]\n+    \"\"\"\n+    pass\n+\n+\n+class MemVoid(MemStruct):\n+    \"\"\"Placeholder for e.g. Ptr to an undetermined type. Useful mostly when\n+    casted to another type. Allows to implement C's \"void*\" pattern.\n+    \"\"\"\n+    def __repr__(self):\n+        return self.__class__.__name__\n+\n+\n+# This does not use _MetaMemStruct features, impl is custom for strings,\n+# because they are unsized. The only memory field is self.value.\n+class MemStr(MemStruct):\n+    \"\"\"Implements a string representation in memory.\n+\n+    The @encoding is passed to the constructor, and is currently either null\n+    terminated \"ansi\" (latin1) or (double) null terminated \"utf16\". Be aware\n+    that the utf16 implementation is a bit buggy...\n+\n+    The string value can be got or set (with python str/unicode) through the\n+    self.value attribute. String encoding/decoding is handled by the class.\n+\n+    This type is dynamically sized only (get_size is implemented, not sizeof).\n+    \"\"\"\n+    def __init__(self, vm, addr, encoding=\"ansi\"):\n+        # TODO: encoding as lambda\n+        if encoding not in [\"ansi\", \"utf16\"]:\n+            raise NotImplementedError(\"Only 'ansi' and 'utf16' are implemented\")\n+        super(MemStr, self).__init__(vm, addr)\n+        self._enc = encoding\n+\n+    @property\n+    def value(self):\n+        \"\"\"Set the string value in memory\"\"\"\n+        if self._enc == \"ansi\":\n+            get_str = get_str_ansi\n+        elif self._enc == \"utf16\":\n+            get_str = get_str_utf16\n+        else:\n+            raise NotImplementedError(\"Only 'ansi' and 'utf16' are implemented\")\n+        return get_str(self._vm, self.get_addr())\n+\n+    @value.setter\n+    def value(self, s):\n+        \"\"\"Get the string value from memory\"\"\"\n+        if self._enc == \"ansi\":\n+            set_str = set_str_ansi\n+        elif self._enc == \"utf16\":\n+            set_str = set_str_utf16\n+        else:\n+            raise NotImplementedError(\"Only 'ansi' and 'utf16' are implemented\")\n+        set_str(self._vm, self.get_addr(), s)\n+\n+    def get_size(self):\n+        \"\"\"This get_size implementation is quite unsafe: it reads the string\n+        underneath to determine the size, it may therefore read a lot of memory\n+        and provoke mem faults (analogous to strlen).\n+        \"\"\"\n+        val = self.value\n+        if self._enc == \"ansi\":\n+            return len(val) + 1\n+        elif self._enc == \"utf16\":\n+            # FIXME: real encoding...\n+            return len(val) * 2 + 2\n+        else:\n+            raise NotImplementedError(\"Only 'ansi' and 'utf16' are implemented\")\n+\n+    def raw(self):\n+        raw = self._vm.get_mem(self.get_addr(), self.get_size())\n+        return raw\n+\n+    def __repr__(self):\n+        return \"%r(%s): %r\" % (self.__class__, self._enc, self.value)\n+\n+\n+class MemArray(MemStruct):\n+    \"\"\"An unsized array of type @field_type (a MemField subclass instance).\n+    This class has no static or dynamic size.\n+\n+    It can be indexed for setting and getting elements, example:\n+\n+        array = MemArray(vm, addr, Num(\"I\"))\n+        array[2] = 5\n+        array[4:8] = [0, 1, 2, 3]\n+        print array[20]\n+\n+    If the @field_type is a Ptr, deref_get(index) and deref_set(index) can be\n+    used to dereference a field at a given index in the array.\n+\n+    mem_array_type can be used to generate a type that includes the field_type.\n+    Such a generated type can be instanciated with only vm and addr, as are\n+    other MemStructs.\n+    \"\"\"\n+    _field_type = None\n+\n+    def __init__(self, vm, addr=None, field_type=None):\n+        if self._field_type is None:\n+            self._field_type = field_type\n+        if self._field_type is None:\n+            raise NotImplementedError(\n+                \"Provide field_type to instanciate this class, \"\n+                \"or generate a subclass with mem_array_type.\")\n+        super(MemArray, self).__init__(vm, addr)\n+\n+    @property\n+    def field_type(self):\n+        \"\"\"Return the MemField subclass instance that represents the type of\n+        this MemArray items.\n+        \"\"\"\n+        return self._field_type\n+\n+    def _normalize_idx(self, idx):\n+        # Noop for this type\n+        return idx\n+\n+    def _normalize_slice(self, slice_):\n+        start = slice_.start if slice_.start is not None else 0\n+        stop = slice_.stop if slice_.stop is not None else self.get_size()\n+        step = slice_.step if slice_.step is not None else 1\n+        return slice(start, stop, step)\n+\n+    def _check_bounds(self, idx):\n+        idx = self._normalize_idx(idx)\n+        if not isinstance(idx, int) and not isinstance(idx, long):\n+            raise ValueError(\"index must be an int or a long\")\n+        if idx < 0:\n+            raise IndexError(\"Index %s out of bounds\" % idx)\n+\n+    def index2addr(self, idx):\n+        \"\"\"Return the address corresponding to a given @index in this MemArray.\n+        \"\"\"\n+        self._check_bounds(idx)\n+        addr = self.get_addr() + idx * self._field_type.size()\n+        return addr\n+\n+    def __getitem__(self, idx):\n+        if isinstance(idx, slice):\n+            res = []\n+            idx = self._normalize_slice(idx)\n+            for i in xrange(idx.start, idx.stop, idx.step):\n+                res.append(self._field_type.get(self._vm, self.index2addr(i)))\n+            return res\n+        else:\n+            return self._field_type.get(self._vm, self.index2addr(idx))\n+\n+    def deref_get(self, idx):\n+        \"\"\"If self.field_type is a Ptr, return the MemStruct self[idx] points\n+        to.\n+        \"\"\"\n+        return self._field_type.deref_get(self._vm, self[idx])\n+\n+    def __setitem__(self, idx, item):\n+        if isinstance(idx, slice):\n+            idx = self._normalize_slice(idx)\n+            if len(item) != len(xrange(idx.start, idx.stop, idx.step)):\n+                raise ValueError(\"Mismatched lengths in slice assignment\")\n+            for i, val in zip(xrange(idx.start, idx.stop, idx.step), item):","path":"miasm2/analysis/mem.py","position":1129,"original_position":1129,"commit_id":"26bdee9a06f6012c2fabfd622b7034030281cf7a","original_commit_id":"26bdee9a06f6012c2fabfd622b7034030281cf7a","user":{"login":"commial","id":4194483,"avatar_url":"https://avatars.githubusercontent.com/u/4194483?v=3","gravatar_id":"","url":"https://api.github.com/users/commial","html_url":"https://github.com/commial","followers_url":"https://api.github.com/users/commial/followers","following_url":"https://api.github.com/users/commial/following{/other_user}","gists_url":"https://api.github.com/users/commial/gists{/gist_id}","starred_url":"https://api.github.com/users/commial/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/commial/subscriptions","organizations_url":"https://api.github.com/users/commial/orgs","repos_url":"https://api.github.com/users/commial/repos","events_url":"https://api.github.com/users/commial/events{/privacy}","received_events_url":"https://api.github.com/users/commial/received_events","type":"User","site_admin":false},"body":"If you want to fully use iterators, you can use `itertools.izip` instead of `zip`","created_at":"2015-11-25T10:18:48Z","updated_at":"2015-11-25T10:18:48Z","html_url":"https://github.com/cea-sec/miasm/pull/284#discussion_r45850036","pull_request_url":"https://api.github.com/repos/cea-sec/miasm/pulls/284","_links":{"self":{"href":"https://api.github.com/repos/cea-sec/miasm/pulls/comments/45850036"},"html":{"href":"https://github.com/cea-sec/miasm/pull/284#discussion_r45850036"},"pull_request":{"href":"https://api.github.com/repos/cea-sec/miasm/pulls/284"}}},"pull_request":{"url":"https://api.github.com/repos/cea-sec/miasm/pulls/284","id":51702266,"html_url":"https://github.com/cea-sec/miasm/pull/284","diff_url":"https://github.com/cea-sec/miasm/pull/284.diff","patch_url":"https://github.com/cea-sec/miasm/pull/284.patch","issue_url":"https://api.github.com/repos/cea-sec/miasm/issues/284","number":284,"state":"open","locked":false,"title":"Feature MemStruct","user":{"login":"fmonjalet","id":6480082,"avatar_url":"https://avatars.githubusercontent.com/u/6480082?v=3","gravatar_id":"","url":"https://api.github.com/users/fmonjalet","html_url":"https://github.com/fmonjalet","followers_url":"https://api.github.com/users/fmonjalet/followers","following_url":"https://api.github.com/users/fmonjalet/following{/other_user}","gists_url":"https://api.github.com/users/fmonjalet/gists{/gist_id}","starred_url":"https://api.github.com/users/fmonjalet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fmonjalet/subscriptions","organizations_url":"https://api.github.com/users/fmonjalet/orgs","repos_url":"https://api.github.com/users/fmonjalet/repos","events_url":"https://api.github.com/users/fmonjalet/events{/privacy}","received_events_url":"https://api.github.com/users/fmonjalet/received_events","type":"User","site_admin":false},"body":"This PR introduces an API to easily interact with C structures in miasm's sandbox.\r\n\r\nThe `example/jitter/memstruct.py` example file may be the best introduction to this feature. As a spoiler, here is how a linked list can be represented with this API (extracted from the aforementioned file):\r\n\r\n```python\r\nclass ListNode(MemStruct):\r\n    fields = [\r\n        # The \"<I\" is the struct-like format of the pointer in memory, in this\r\n        # case a Little Endian 32 bits unsigned int\r\n        # One way to handle reference to ListNode in ListNode is to use the\r\n        # special marker MemSelf.\r\n        # You could also set or modify ListNode.fields after the class\r\n        # declaration and call ListNode.gen_fields()\r\n        (\"next\", Ptr(\"<I\", MemSelf)),\r\n        # Ptr(_, MemVoid) is analogous to void*, MemVoid is just an empty\r\n        # MemStruct type\r\n        (\"data\", Ptr(\"<I\", MemVoid)),\r\n    ]\r\n\r\nclass LinkedList(MemStruct):\r\n    fields = [\r\n        (\"head\", Ptr(\"<I\", ListNode)),\r\n        (\"tail\", Ptr(\"<I\", ListNode)),\r\n        # Num can take any one-field struct-like format, including floats and\r\n        # doubles\r\n        (\"size\", Num(\"<I\")),\r\n    ]\r\n\r\n# [...]\r\n\r\nlink = LinkedList(jitter.vm, some_addr)\r\nlink.deref_head.data = other_addr\r\nlink.size += 1\r\n# etc\r\n```\r\n\r\nLots of FIXME/TODO are left there for now and lots of choices can be discussed, please tell me what you think!\r\n\r\n-- Florent","created_at":"2015-11-24T19:03:36Z","updated_at":"2015-11-25T10:18:48Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ac30b55bec06e52ef7ba66dca1e0f01cdfcb72e2","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/cea-sec/miasm/pulls/284/commits","review_comments_url":"https://api.github.com/repos/cea-sec/miasm/pulls/284/comments","review_comment_url":"https://api.github.com/repos/cea-sec/miasm/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cea-sec/miasm/issues/284/comments","statuses_url":"https://api.github.com/repos/cea-sec/miasm/statuses/26bdee9a06f6012c2fabfd622b7034030281cf7a","head":{"label":"fmonjalet:feature_memstruct","ref":"feature_memstruct","sha":"26bdee9a06f6012c2fabfd622b7034030281cf7a","user":{"login":"fmonjalet","id":6480082,"avatar_url":"https://avatars.githubusercontent.com/u/6480082?v=3","gravatar_id":"","url":"https://api.github.com/users/fmonjalet","html_url":"https://github.com/fmonjalet","followers_url":"https://api.github.com/users/fmonjalet/followers","following_url":"https://api.github.com/users/fmonjalet/following{/other_user}","gists_url":"https://api.github.com/users/fmonjalet/gists{/gist_id}","starred_url":"https://api.github.com/users/fmonjalet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fmonjalet/subscriptions","organizations_url":"https://api.github.com/users/fmonjalet/orgs","repos_url":"https://api.github.com/users/fmonjalet/repos","events_url":"https://api.github.com/users/fmonjalet/events{/privacy}","received_events_url":"https://api.github.com/users/fmonjalet/received_events","type":"User","site_admin":false},"repo":{"id":28982751,"name":"miasm","full_name":"fmonjalet/miasm","owner":{"login":"fmonjalet","id":6480082,"avatar_url":"https://avatars.githubusercontent.com/u/6480082?v=3","gravatar_id":"","url":"https://api.github.com/users/fmonjalet","html_url":"https://github.com/fmonjalet","followers_url":"https://api.github.com/users/fmonjalet/followers","following_url":"https://api.github.com/users/fmonjalet/following{/other_user}","gists_url":"https://api.github.com/users/fmonjalet/gists{/gist_id}","starred_url":"https://api.github.com/users/fmonjalet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fmonjalet/subscriptions","organizations_url":"https://api.github.com/users/fmonjalet/orgs","repos_url":"https://api.github.com/users/fmonjalet/repos","events_url":"https://api.github.com/users/fmonjalet/events{/privacy}","received_events_url":"https://api.github.com/users/fmonjalet/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/fmonjalet/miasm","description":"Reverse engineering framework in Python","fork":true,"url":"https://api.github.com/repos/fmonjalet/miasm","forks_url":"https://api.github.com/repos/fmonjalet/miasm/forks","keys_url":"https://api.github.com/repos/fmonjalet/miasm/keys{/key_id}","collaborators_url":"https://api.github.com/repos/fmonjalet/miasm/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/fmonjalet/miasm/teams","hooks_url":"https://api.github.com/repos/fmonjalet/miasm/hooks","issue_events_url":"https://api.github.com/repos/fmonjalet/miasm/issues/events{/number}","events_url":"https://api.github.com/repos/fmonjalet/miasm/events","assignees_url":"https://api.github.com/repos/fmonjalet/miasm/assignees{/user}","branches_url":"https://api.github.com/repos/fmonjalet/miasm/branches{/branch}","tags_url":"https://api.github.com/repos/fmonjalet/miasm/tags","blobs_url":"https://api.github.com/repos/fmonjalet/miasm/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/fmonjalet/miasm/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/fmonjalet/miasm/git/refs{/sha}","trees_url":"https://api.github.com/repos/fmonjalet/miasm/git/trees{/sha}","statuses_url":"https://api.github.com/repos/fmonjalet/miasm/statuses/{sha}","languages_url":"https://api.github.com/repos/fmonjalet/miasm/languages","stargazers_url":"https://api.github.com/repos/fmonjalet/miasm/stargazers","contributors_url":"https://api.github.com/repos/fmonjalet/miasm/contributors","subscribers_url":"https://api.github.com/repos/fmonjalet/miasm/subscribers","subscription_url":"https://api.github.com/repos/fmonjalet/miasm/subscription","commits_url":"https://api.github.com/repos/fmonjalet/miasm/commits{/sha}","git_commits_url":"https://api.github.com/repos/fmonjalet/miasm/git/commits{/sha}","comments_url":"https://api.github.com/repos/fmonjalet/miasm/comments{/number}","issue_comment_url":"https://api.github.com/repos/fmonjalet/miasm/issues/comments{/number}","contents_url":"https://api.github.com/repos/fmonjalet/miasm/contents/{+path}","compare_url":"https://api.github.com/repos/fmonjalet/miasm/compare/{base}...{head}","merges_url":"https://api.github.com/repos/fmonjalet/miasm/merges","archive_url":"https://api.github.com/repos/fmonjalet/miasm/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/fmonjalet/miasm/downloads","issues_url":"https://api.github.com/repos/fmonjalet/miasm/issues{/number}","pulls_url":"https://api.github.com/repos/fmonjalet/miasm/pulls{/number}","milestones_url":"https://api.github.com/repos/fmonjalet/miasm/milestones{/number}","notifications_url":"https://api.github.com/repos/fmonjalet/miasm/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/fmonjalet/miasm/labels{/name}","releases_url":"https://api.github.com/repos/fmonjalet/miasm/releases{/id}","created_at":"2015-01-08T19:43:51Z","updated_at":"2015-03-23T20:49:23Z","pushed_at":"2015-11-24T18:30:09Z","git_url":"git://github.com/fmonjalet/miasm.git","ssh_url":"git@github.com:fmonjalet/miasm.git","clone_url":"https://github.com/fmonjalet/miasm.git","svn_url":"https://github.com/fmonjalet/miasm","homepage":null,"size":10578,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"cea-sec:master","ref":"master","sha":"7c74ea27685aabb51bf158b5ca13ce8fec147ef0","user":{"login":"cea-sec","id":5071925,"avatar_url":"https://avatars.githubusercontent.com/u/5071925?v=3","gravatar_id":"","url":"https://api.github.com/users/cea-sec","html_url":"https://github.com/cea-sec","followers_url":"https://api.github.com/users/cea-sec/followers","following_url":"https://api.github.com/users/cea-sec/following{/other_user}","gists_url":"https://api.github.com/users/cea-sec/gists{/gist_id}","starred_url":"https://api.github.com/users/cea-sec/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cea-sec/subscriptions","organizations_url":"https://api.github.com/users/cea-sec/orgs","repos_url":"https://api.github.com/users/cea-sec/repos","events_url":"https://api.github.com/users/cea-sec/events{/privacy}","received_events_url":"https://api.github.com/users/cea-sec/received_events","type":"Organization","site_admin":false},"repo":{"id":25875083,"name":"miasm","full_name":"cea-sec/miasm","owner":{"login":"cea-sec","id":5071925,"avatar_url":"https://avatars.githubusercontent.com/u/5071925?v=3","gravatar_id":"","url":"https://api.github.com/users/cea-sec","html_url":"https://github.com/cea-sec","followers_url":"https://api.github.com/users/cea-sec/followers","following_url":"https://api.github.com/users/cea-sec/following{/other_user}","gists_url":"https://api.github.com/users/cea-sec/gists{/gist_id}","starred_url":"https://api.github.com/users/cea-sec/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cea-sec/subscriptions","organizations_url":"https://api.github.com/users/cea-sec/orgs","repos_url":"https://api.github.com/users/cea-sec/repos","events_url":"https://api.github.com/users/cea-sec/events{/privacy}","received_events_url":"https://api.github.com/users/cea-sec/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cea-sec/miasm","description":"Reverse engineering framework in Python","fork":false,"url":"https://api.github.com/repos/cea-sec/miasm","forks_url":"https://api.github.com/repos/cea-sec/miasm/forks","keys_url":"https://api.github.com/repos/cea-sec/miasm/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cea-sec/miasm/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cea-sec/miasm/teams","hooks_url":"https://api.github.com/repos/cea-sec/miasm/hooks","issue_events_url":"https://api.github.com/repos/cea-sec/miasm/issues/events{/number}","events_url":"https://api.github.com/repos/cea-sec/miasm/events","assignees_url":"https://api.github.com/repos/cea-sec/miasm/assignees{/user}","branches_url":"https://api.github.com/repos/cea-sec/miasm/branches{/branch}","tags_url":"https://api.github.com/repos/cea-sec/miasm/tags","blobs_url":"https://api.github.com/repos/cea-sec/miasm/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cea-sec/miasm/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cea-sec/miasm/git/refs{/sha}","trees_url":"https://api.github.com/repos/cea-sec/miasm/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cea-sec/miasm/statuses/{sha}","languages_url":"https://api.github.com/repos/cea-sec/miasm/languages","stargazers_url":"https://api.github.com/repos/cea-sec/miasm/stargazers","contributors_url":"https://api.github.com/repos/cea-sec/miasm/contributors","subscribers_url":"https://api.github.com/repos/cea-sec/miasm/subscribers","subscription_url":"https://api.github.com/repos/cea-sec/miasm/subscription","commits_url":"https://api.github.com/repos/cea-sec/miasm/commits{/sha}","git_commits_url":"https://api.github.com/repos/cea-sec/miasm/git/commits{/sha}","comments_url":"https://api.github.com/repos/cea-sec/miasm/comments{/number}","issue_comment_url":"https://api.github.com/repos/cea-sec/miasm/issues/comments{/number}","contents_url":"https://api.github.com/repos/cea-sec/miasm/contents/{+path}","compare_url":"https://api.github.com/repos/cea-sec/miasm/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cea-sec/miasm/merges","archive_url":"https://api.github.com/repos/cea-sec/miasm/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cea-sec/miasm/downloads","issues_url":"https://api.github.com/repos/cea-sec/miasm/issues{/number}","pulls_url":"https://api.github.com/repos/cea-sec/miasm/pulls{/number}","milestones_url":"https://api.github.com/repos/cea-sec/miasm/milestones{/number}","notifications_url":"https://api.github.com/repos/cea-sec/miasm/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cea-sec/miasm/labels{/name}","releases_url":"https://api.github.com/repos/cea-sec/miasm/releases{/id}","created_at":"2014-10-28T15:05:47Z","updated_at":"2015-11-25T03:56:48Z","pushed_at":"2015-11-24T19:03:37Z","git_url":"git://github.com/cea-sec/miasm.git","ssh_url":"git@github.com:cea-sec/miasm.git","clone_url":"https://github.com/cea-sec/miasm.git","svn_url":"https://github.com/cea-sec/miasm","homepage":null,"size":11096,"stargazers_count":519,"watchers_count":519,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":70,"mirror_url":null,"open_issues_count":16,"forks":70,"open_issues":16,"watchers":519,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cea-sec/miasm/pulls/284"},"html":{"href":"https://github.com/cea-sec/miasm/pull/284"},"issue":{"href":"https://api.github.com/repos/cea-sec/miasm/issues/284"},"comments":{"href":"https://api.github.com/repos/cea-sec/miasm/issues/284/comments"},"review_comments":{"href":"https://api.github.com/repos/cea-sec/miasm/pulls/284/comments"},"review_comment":{"href":"https://api.github.com/repos/cea-sec/miasm/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cea-sec/miasm/pulls/284/commits"},"statuses":{"href":"https://api.github.com/repos/cea-sec/miasm/statuses/26bdee9a06f6012c2fabfd622b7034030281cf7a"}}}}