diff options
| -rw-r--r-- | example/jitter/memstruct.py | 29 | ||||
| -rw-r--r-- | miasm2/analysis/mem.py | 480 | ||||
| -rw-r--r-- | test/analysis/mem.py | 246 |
3 files changed, 407 insertions, 348 deletions
diff --git a/example/jitter/memstruct.py b/example/jitter/memstruct.py index 645c019e..6e8e13af 100644 --- a/example/jitter/memstruct.py +++ b/example/jitter/memstruct.py @@ -36,15 +36,15 @@ class ListNode(PinnedStruct): ] def get_next(self): - if self.next == 0: + if self.next.val == 0: return None - return self.deref_next + return self.next.deref def get_data(self, data_type=None): if data_type is not None: - return self.deref_data.cast(data_type) + return self.data.deref.cast(data_type) else: - return self.deref_data + return self.data.deref class LinkedList(PinnedStruct): @@ -60,12 +60,12 @@ class LinkedList(PinnedStruct): """Returns the head ListNode instance""" if self.head == 0: return None - return self.deref_head + return self.head.deref def get_tail(self): if self.tail == 0: return None - return self.deref_tail + return self.tail.deref def push(self, data): # Allocate a new node @@ -112,7 +112,7 @@ class LinkedList(PinnedStruct): if not self.empty(): cur = self.get_head() while cur is not None: - yield cur.deref_data + yield cur.data.deref cur = cur.get_next() @@ -123,9 +123,9 @@ class DataArray(PinnedStruct): ("val1", Num("B")), ("val2", Num("B")), # Ptr can also be instanciated with a PinnedField as an argument, a special - # PinnedStruct containing only one field named "value" will be created, so + # PinnedStruct containing only one field named "val" will be created, so # that Ptr can point to a PinnedStruct instance. Here, - # data_array.deref_array.value will allow to access an Array + # data_array.array.deref.val will allow to access an Array ("arrayptr", Ptr("<I", PinnedSizedArray, Num("B"), 16)), # Array of 10 uint8 ("array", Array(Num("B"), 16)), @@ -163,7 +163,8 @@ link.push(DataArray(vm)) # Size has been updated assert link.size == 3 # If you get it directly from the VM, it is updated as well -raw_size = vm.get_mem(link.get_addr("size"), link.get_field_type("size").size()) +raw_size = vm.get_mem(link.get_addr("size"), link.get_type() + .get_field_type("size").size()) assert raw_size == '\x03\x00\x00\x00' print "The linked list just built:" @@ -182,7 +183,7 @@ assert link.size == 2 # Make the Array Ptr point to the data's array field data.arrayptr = data.get_addr("array") # Now the pointer dereference is equal to the array field's value -assert data.deref_arrayptr == data.array +assert data.arrayptr.deref == data.array # Let's say that it is a DataStr: datastr = data.cast(DataStr) @@ -201,9 +202,9 @@ datastr.valshort = 0x1122 assert data.val1 == 0x22 and data.val2 == 0x11 # Let's play with strings -memstr = datastr.deref_data +memstr = datastr.data.deref # Note that memstr is PinnedStr(..., "utf16") -memstr.value = 'Miams' +memstr.val = 'Miams' print "Cast data.array to PinnedStr and set the string value:" print repr(memstr) @@ -216,7 +217,7 @@ assert list(data.array)[:len(raw_miams_array)] == raw_miams_array assert data.array.cast(PinnedStr, "utf16") == memstr # Default is "ansi" assert data.array.cast(PinnedStr) != memstr -assert data.array.cast(PinnedStr, "utf16").value == memstr.value +assert data.array.cast(PinnedStr, "utf16").val == memstr.val print "See that the original array has been modified:" print repr(data) diff --git a/miasm2/analysis/mem.py b/miasm2/analysis/mem.py index 2c5e488c..2b2b3a72 100644 --- a/miasm2/analysis/mem.py +++ b/miasm2/analysis/mem.py @@ -72,7 +72,7 @@ of PinnedType with a custom implementation: - PinnedSizedArray: a sized PinnedArray, can be automatically allocated in memory and allows more operations than PinnedArray - pin: a function that dynamically generates a PinnedStruct subclass from a - Type. This class has only one field named "value". + Type. This class has only one field named "val". A PinnedType do not always have a static size (cls.sizeof()) nor a dynamic size (self.get_size()). @@ -171,14 +171,14 @@ def set_str_utf16(vm, addr, s): def pin(field): """Generate a PinnedStruct subclass from a field. The field's value can - be accessed through self.value or self.deref_value if field is a Ptr. + be accessed through self.val or self.deref_val if field is a Ptr. @field: a Type instance. """ if field in DYN_MEM_STRUCT_CACHE: return DYN_MEM_STRUCT_CACHE[field] - fields = [("value", field)] + fields = [("val", field)] # Build a type to contain the field type mem_type = type("Pinned%r" % field, (PinnedStruct,), {'fields': fields}) DYN_MEM_STRUCT_CACHE[field] = mem_type @@ -316,7 +316,11 @@ class Num(RawStruct): class Ptr(Num): """Special case of number of which value indicates the address of a - PinnedType. Provides deref_<field> as well as <field> when used, to set and + PinnedType. + + FIXME: DOC + + Provides deref_<field> as well as <field> when used, to set and get the pointed PinnedType. """ @@ -360,7 +364,7 @@ class Ptr(Num): else: raise ValueError("Unsupported usecase for PinnedSelf, sorry") if isinstance(self._dst_type, Type): - self._dst_type = pin(self._dst_type) + self._dst_type = self._dst_type.pinned @property def dst_type(self): @@ -368,11 +372,28 @@ class Ptr(Num): self._fix_dst_type() return self._dst_type + def set(self, vm, addr, val): + if isinstance(val, PinnedType) and isinstance(val.get_type(), Ptr): + self.set_val(vm, addr, val.val) + else: + super(Ptr, self).set(vm, addr, val) + + def get(self, vm, addr): + return self.pinned(vm, addr) + + def get_val(self, vm, addr): + return super(Ptr, self).get(vm, addr) + + def set_val(self, vm, addr, val): + return super(Ptr, self).set(vm, addr, val) + def deref_get(self, vm, addr): """Deserializes the data in @vm (VmMngr) at @addr to self.dst_type. Equivalent to a pointer dereference rvalue in C. """ - return self.dst_type(vm, addr, *self._type_args, **self._type_kwargs) + dst_addr = self.get_val(vm, addr) + return self.dst_type(vm, dst_addr, + *self._type_args, **self._type_kwargs) def deref_set(self, vm, addr, val): """Serializes the @val PinnedType subclass instance in @vm (VmMngr) at @@ -384,7 +405,8 @@ class Ptr(Num): self._dst_type.__name__, val.__class__.__name__) # Actual job - vm.set_mem(addr, str(val)) + dst_addr = self.get_val(vm, addr) + vm.set_mem(dst_addr, str(val)) def _get_pinned_base_class(self): return PinnedPtr @@ -403,7 +425,7 @@ class Ptr(Num): self._type_args)) -class Inline(Type): +class Struct(Type): """Field used to inline a PinnedType in another PinnedType. Equivalent to having a struct field in a C struct. @@ -424,34 +446,145 @@ class Inline(Type): TODO: make the Inline implicit when setting a field to be a PinnedStruct """ - def __init__(self, inlined_type, *type_args, **type_kwargs): - if not issubclass(inlined_type, PinnedStruct): - raise ValueError("inlined type if Inline must be a PinnedStruct") - self._il_type = inlined_type - self._type_args = type_args - self._type_kwargs = type_kwargs + def __init__(self, name, fields): + self.name = name + # fields is immutable + self._fields = tuple(fields) + self._gen_fields() + + def _gen_fields(self): + """Precompute useful metadata on self.fields.""" + self._fields_desc = {} + offset = 0 + for name, field in self._fields: + # For reflexion + field._set_self_type(self) + self._gen_field(name, field, offset) + offset += field.size() + self._size = offset + + def _gen_field(self, name, field, offset): + """Generate only one field + + @name: (str) the name of the field + @field: (Type instance) the field type + @offset: (int) the offset of the field in the structure + """ + self._fields_desc[name] = {"field": field, "offset": offset} + + @property + def fields(self): + return self._fields def set(self, vm, addr, val): raw = str(val) vm.set_mem(addr, raw) def get(self, vm, addr): - return self._il_type(vm, addr) + return self.pinned(vm, addr) + + def get_field(self, vm, addr, name): + """get a field value by name. + + useless most of the time since fields are accessible via self.<name>. + """ + if name not in self._fields_desc: + raise ValueError("'%s' type has no field '%s'" + % (self, name)) + field = self.get_field_type(name) + offset = self.get_offset(name) + return field.get(vm, addr + offset) + + def set_field(self, vm, addr, name, val): + """set a field value by name. @val is the python value corresponding to + this field type. + + useless most of the time since fields are accessible via self.<name>. + """ + if name not in self._fields_desc: + raise AttributeError("'%s' object has no attribute '%s'" + % (self.__class__.__name__, name)) + field = self.get_field_type(name) + offset = self.get_offset(name) + field.set(vm, addr + offset, val) def size(self): - return self._il_type.sizeof() + # Child classes can set self._size if their size is not the sum of + # their fields + return sum(a["field"].size() for a in self._fields_desc.itervalues()) + + def get_offset(self, field_name): + """ + @field_name: (str, optional) the name of the field to get the + offset of + """ + if field_name not in self._fields_desc: + raise ValueError("This structure has no %s field" % field_name) + return self._fields_desc[field_name]['offset'] + + def get_field_type(self, name): + """return the type subclass instance describing field @name.""" + # TODO: move it to Struct + return self._fields_desc[name]['field'] + + #def _build_pinned_type(self): + # mem_type = type("PinnedStruct%s" % self.name, + # (PinnedStruct,), + # {'_type': self}) + # return mem_type + + def _get_pinned_base_class(self): + return PinnedStruct def __repr__(self): - return "%s(%r)" % (self.__class__.__name__, self._il_type) + return "Struct%s" % self.name def __eq__(self, other): return self.__class__ == other.__class__ and \ - self._il_type == other._il_type and \ - self._type_args == other._type_args and \ - self._type_kwargs == other._type_kwargs + self.fields == other.fields and \ + self.name == other.name def __hash__(self): - return hash((self.__class__, self._il_type, self._type_args)) + # Only hash name, not fields, because if a field is a Ptr to this + # Struct type, an infinite loop occurs + return hash((self.__class__, self.name)) + + +class Union(Struct): + """Allows to put multiple fields at the same offset in a PinnedStruct, similar + to unions in C. The Union will have the size of the largest of its fields. + + Example: + + class Example(PinnedStruct): + fields = [("uni", Union([ + ("f1", Num("<B")), + ("f2", Num("<H")) + ]) + )] + + ex = Example(vm, addr) + ex.uni.f2 = 0x1234 + assert ex.uni.f1 == 0x34 + """ + + def __init__(self, field_list): + """field_list is a [(name, field)] list, see the class doc""" + super(Union, self).__init__("union", field_list) + + def size(self): + return max(field.size() for _, field in self.fields) + + def get_offset(self, field_name): + return 0 + + def _get_pinned_base_class(self): + return PinnedUnion + + def __repr__(self): + fields_repr = ', '.join("%s: %r" % (name, field) + for name, field in self.fields) + return "%s(%s)" % (self.__class__.__name__, fields_repr) class Array(Type): @@ -529,56 +662,6 @@ class Array(Type): return hash((self.__class__, self.field_type, self.array_len)) -# TODO: PinnedUnion -class Union(Type): - """Allows to put multiple fields at the same offset in a PinnedStruct, similar - to unions in C. The Union will have the size of the largest of its fields. - - Example: - - class Example(PinnedStruct): - fields = [("uni", Union([ - ("f1", Num("<B")), - ("f2", Num("<H")) - ]) - )] - - ex = Example(vm, addr) - ex.f2 = 0x1234 - assert ex.f1 == 0x34 - assert ex.uni == '\x34\x12' - assert ex.get_addr("f1") == ex.get_addr("f2") - """ - - def __init__(self, field_list): - """field_list is a [(name, field)] list, see the class doc""" - self.field_list = field_list - - def size(self): - return max(field.size() for _, field in self.field_list) - - def set(self, vm, addr, val): - if not isinstance(val, str) or not len(str) == self.size(): - raise ValueError("Union can only be set with raw str of the Union's" - " size") - vm.set_mem(vm, addr, val) - - def get(self, vm, addr): - return vm.get_mem(addr, self.size()) - - def __repr__(self): - fields_repr = ', '.join("%s: %r" % (name, field) - for name, field in self.field_list) - return "%s(%s)" % (self.__class__.__name__, fields_repr) - - def __eq__(self, other): - return self.__class__ == other.__class__ and \ - self.field_list == other.field_list - - def __hash__(self): - return hash((self.__class__, tuple(self.field_list))) - - class Bits(Type): """Helper class for BitField, not very useful on its own. Represents some bits of a Num. @@ -686,9 +769,6 @@ class BitField(Union): def set(self, vm, addr, val): self._num.set(vm, addr, val) - def get(self, vm, addr): - return self._num.get(vm, addr) - def __eq__(self, other): return self.__class__ == other.__class__ and \ self._num == other._num and super(BitField, self).__eq__(other) @@ -697,9 +777,13 @@ class BitField(Union): return hash((super(BitField, self).__hash__(), self._num)) +# TODO +class Str(Type): + pass + class Void(Type): """Represents the C void type.""" - + def _build_pinned_type(self): return PinnedVoid @@ -727,19 +811,20 @@ class _MetaPinnedStruct(_MetaPinnedType): def __init__(cls, name, bases, dct): super(_MetaPinnedStruct, cls).__init__(name, bases, dct) - cls.gen_fields() + if cls.fields is not None: + cls.fields = tuple(cls.fields) + if cls.get_type() is not None or cls.fields is not None: + cls.gen_fields() class PinnedType(object): __metaclass__ = _MetaPinnedType _size = None + _type = None - # Classic usage methods - - def __init__(self, vm, addr=None, *args, **kwargs): + def __init__(self, vm, addr=None, type_=None): global ALLOCATOR - super(PinnedType, self).__init__(*args, **kwargs) self._vm = vm if addr is None: if ALLOCATOR is None: @@ -749,6 +834,11 @@ class PinnedType(object): self._addr = ALLOCATOR(vm, self.get_size()) else: self._addr = addr + if type_ is not None: + self._type = type_ + if self._type is None: + raise ValueError("Subclass PinnedType and define cls._type or pass " + "a type to the constructor") def get_addr(self, field=None): """Return the address of this PinnedType or one of its fields. @@ -759,10 +849,17 @@ class PinnedType(object): return self._addr @classmethod + def get_type(cls): + """Returns the Type instance representing the C type of this PinnedType. + """ + return cls._type + + @classmethod def sizeof(cls): - """ABSTRACT Return the static size of this type. + """Return the static size of this type. By default, it is the size + of the underlying type. """ - raise NotImplementedError("Abstract") + return cls._type.size() def get_size(self): """Return the dynamic size of this structure (e.g. the size of an @@ -811,15 +908,7 @@ class PinnedType(object): return self.raw() def __repr__(self): - attrs = sorted(self._attrs.iteritems(), key=lambda a: a[1]["offset"]) - out = [] - for name, attr in attrs: - field = attr["field"] - val_repr = repr(self.get_field(name)) - if '\n' in val_repr: - val_repr = '\n' + indent(val_repr, 4) - out.append("%s: %r = %s" % (name, field, val_repr)) - return '%r:\n' % self.__class__ + indent('\n'.join(out), 2) + return "Pinned%r" % self._type def __eq__(self, other): return self.__class__ == other.__class__ and str(self) == str(other) @@ -829,15 +918,6 @@ class PinnedType(object): class PinnedValue(PinnedType): - _type = None - - def __init__(self, vm, addr=None, type_=None): - super(PinnedValue, self).__init__(vm, addr) - if type_ is not None: - self._type = type_ - if self._type is None: - raise ValueError("Subclass PinnedPtr and define cls._type or pass a" - " Ptr to the constructor") @property def val(self): @@ -895,16 +975,7 @@ class PinnedStruct(PinnedType): See the various Type doc for more information. """ __metaclass__ = _MetaPinnedStruct - - fields = [] - - @classmethod - def sizeof(cls): - # Child classes can set cls._size if their size is not the sum of - # their fields - if cls._size is None: - return sum(a["field"].size() for a in cls._attrs.itervalues()) - return cls._size + fields = None def get_addr(self, field_name=None): """ @@ -912,28 +983,17 @@ class PinnedStruct(PinnedType): address of """ if field_name is not None: - if field_name not in self._attrs: - raise ValueError("This structure has no %s field" % field_name) - offset = self._attrs[field_name]['offset'] + offset = self._type.get_offset(field_name) else: offset = 0 return self._addr + offset - def get_field_type(self, name): - """return the type subclass instance describing field @name.""" - return self._attrs[name]['field'] - def get_field(self, name): """get a field value by name. useless most of the time since fields are accessible via self.<name>. """ - if name not in self._attrs: - raise attributeerror("'%s' object has no attribute '%s'" - % (self.__class__.__name__, name)) - field = self._attrs[name]["field"] - offset = self._attrs[name]["offset"] - return field.get(self._vm, self.get_addr() + offset) + return self._type.get_field(self._vm, self.get_addr(), name) def set_field(self, name, val): """set a field value by name. @val is the python value corresponding to @@ -941,37 +1001,7 @@ class PinnedStruct(PinnedType): useless most of the time since fields are accessible via self.<name>. """ - if name not in self._attrs: - raise attributeerror("'%s' object has no attribute '%s'" - % (self.__class__.__name__, name)) - field = self._attrs[name]["field"] - offset = self._attrs[name]["offset"] - field.set(self._vm, self.get_addr() + offset, val) - - def deref_field(self, name): - """get the memstruct pointed by <name> field. - - useless most of the time since fields are accessible via - self.deref_<name>. - """ - addr = self.get_field(name) - field = self._attrs[name]["field"] - assert isinstance(field, Ptr),\ - "programming error: field should be a Ptr" - return field.deref_get(self._vm, addr) - - def set_deref_field(self, name, val): - """set the memstruct pointed by <name> field. @val should be of the - type of the pointed memstruct. the field must be a Ptr. - - useless most of the time since fields are accessible via - self.deref_<name>. - """ - addr = self.get_field(name) - field = self._attrs[name]["field"] - assert isinstance(field, Ptr),\ - "programming error: field should be a Ptr" - field.deref_set(self._vm, addr, val) + return self._type.set_field(self._vm, self.get_addr(), name, val) def cast_field(self, field, other_type, *type_args, **type_kwargs): """ @@ -981,7 +1011,7 @@ class PinnedStruct(PinnedType): *type_args, **type_kwargs) - # Field generation methods, voluntarily public to be able to regen fields + # Field generation methods, voluntarily public to be able to gen fields # after class definition @classmethod @@ -1006,56 +1036,59 @@ class PinnedStruct(PinnedType): class B(PinnedStruct): fields = [("a", Ptr("I", A))] - A.fields = [("b", Ptr("I", B))] - a.gen_field() + A.gen_fields([("b", Ptr("I", B))]) """ - if fields is None: - fields = cls.fields - cls._attrs = {} - offset = 0 - for name, field in cls.fields: - # For reflexion - field._set_self_type(cls) - cls.gen_field(name, field, offset) - offset += field.size() - cls._size = offset + if fields is not None: + if cls.fields is not None: + raise ValueError("Cannot regen fields of a class. Setting " + "cls.fields at class definition and calling " + "gen_fields are mutually exclusive.") + cls.fields = fields + + if cls._type is None: + if cls.fields is None: + raise ValueError("Cannot create a PinnedStruct subclass without" + " a cls._type or a cls.fields") + cls._type = cls._gen_type(cls.fields) + + if cls._type in DYN_MEM_STRUCT_CACHE: + # FIXME: Maybe a warning would be better? + raise RuntimeError("Another PinnedType has the same type as this " + "one. Use it instead.") + + # Register this class so that another one will not be created when + # calling cls._type.pinned + DYN_MEM_STRUCT_CACHE[cls._type] = cls + + cls._gen_attributes() @classmethod - def gen_field(cls, name, field, offset): - """Generate only one field - - @name: (str) the name of the field - @field: (Type instance) the field type - @offset: (int) the offset of the field in the structure - """ - cls._gen_simple_attr(name, field, offset) - if isinstance(field, Union): - cls._gen_union_attr(field, offset) + def _gen_attributes(cls): + # Generate self.<name> getter and setters + for name, field in cls._type.fields: + setattr(cls, name, property( + lambda self, name=name: self.get_field(name), + lambda self, val, name=name: self.set_field(name, val) + )) @classmethod - def _gen_simple_attr(cls, name, field, offset): - cls._attrs[name] = {"field": field, "offset": offset} - - # Generate self.<name> getter and setter - setattr(cls, name, property( - lambda self: self.get_field(name), - lambda self, val: self.set_field(name, val) - )) - - # Generate self.deref_<name> getter and setter if this field is a - # Ptr - if isinstance(field, Ptr): - setattr(cls, "deref_%s" % name, property( - lambda self: self.deref_field(name), - lambda self, val: self.set_deref_field(name, val) - )) + def _gen_type(cls, fields): + return Struct(cls.__name__, fields) + + def __repr__(self): + out = [] + for name, field in self._type.fields: + val_repr = repr(self.get_field(name)) + if '\n' in val_repr: + val_repr = '\n' + indent(val_repr, 4) + out.append("%s: %r = %s" % (name, field, val_repr)) + return '%r:\n' % self.__class__ + indent('\n'.join(out), 2) + +class PinnedUnion(PinnedStruct): @classmethod - def _gen_union_attr(cls, union_field, offset): - if not isinstance(union_field, Union): - raise ValueError("field should be an Union instance") - for name, field in union_field.field_list: - cls.gen_field(name, field, offset) + def _gen_type(cls, fields): + return Union(fields) class PinnedSelf(PinnedStruct): @@ -1069,13 +1102,16 @@ class PinnedSelf(PinnedStruct): ("data", Ptr("<I", PinnedVoid)), ] """ - pass + def __repr__(self): + return self.__class__.__name__ class PinnedVoid(PinnedType): """Placeholder for e.g. Ptr to an undetermined type. Useful mostly when casted to another type. Allows to implement C's "void*" pattern. """ + _type = Void() + def __repr__(self): return self.__class__.__name__ @@ -1083,6 +1119,14 @@ class PinnedVoid(PinnedType): class PinnedPtr(PinnedValue): @property + def val(self): + return self._type.get_val(self._vm, self._addr) + + @val.setter + def val(self, value): + return self._type.set_val(self._vm, self._addr, value) + + @property def deref(self): return self._type.deref_get(self._vm, self._addr) @@ -1090,6 +1134,10 @@ class PinnedPtr(PinnedValue): def deref(self, val): return self._type.deref_set(self._vm, self._addr, val) + def __repr__(self): + return "*%s" % hex(self.val) + + # This does not use _MetaPinnedStruct features, impl is custom for strings, # because they are unsized. The only memory field is self.value. @@ -1101,7 +1149,7 @@ class PinnedStr(PinnedType): that the utf16 implementation is a bit buggy... The string value can be got or set (with python str/unicode) through the - self.value attribute. String encoding/decoding is handled by the class. + self.val attribute. String encoding/decoding is handled by the class. This type is dynamically sized only (get_size is implemented, not sizeof). """ @@ -1109,11 +1157,12 @@ class PinnedStr(PinnedType): # TODO: encoding as lambda if encoding not in ["ansi", "utf16"]: raise NotImplementedError("Only 'ansi' and 'utf16' are implemented") - super(PinnedStr, self).__init__(vm, addr) + # FIXME: Str type + super(PinnedStr, self).__init__(vm, addr, Str()) self._enc = encoding @property - def value(self): + def val(self): """Set the string value in memory""" if self._enc == "ansi": get_str = get_str_ansi @@ -1123,8 +1172,8 @@ class PinnedStr(PinnedType): raise NotImplementedError("Only 'ansi' and 'utf16' are implemented") return get_str(self._vm, self.get_addr()) - @value.setter - def value(self, s): + @val.setter + def val(self, s): """Get the string value from memory""" if self._enc == "ansi": set_str = set_str_ansi @@ -1139,7 +1188,7 @@ class PinnedStr(PinnedType): underneath to determine the size, it may therefore read a lot of memory and provoke mem faults (analogous to strlen). """ - val = self.value + val = self.val if self._enc == "ansi": return len(val) + 1 elif self._enc == "utf16": @@ -1153,7 +1202,7 @@ class PinnedStr(PinnedType): return raw def __repr__(self): - return "%r(%s): %r" % (self.__class__, self._enc, self.value) + return "%r(%s): %r" % (self.__class__, self._enc, self.val) class PinnedArray(PinnedType): @@ -1167,9 +1216,6 @@ class PinnedArray(PinnedType): array[4:8] = [0, 1, 2, 3] print array[20] - If the @field_type is a Ptr, deref_get(index) and deref_set(index) can be - used to dereference a field at a given index in the array. - mem_array_type can be used to generate a type that includes the field_type. Such a generated type can be instanciated with only vm and addr, as are other PinnedTypes. @@ -1183,7 +1229,9 @@ class PinnedArray(PinnedType): raise NotImplementedError( "Provide field_type to instanciate this class, " "or generate a subclass with mem_array_type.") - super(PinnedArray, self).__init__(vm, addr) + # FIXME: use underlying Array type + super(PinnedArray, self).__init__(vm, addr, + Array(self._field_type, None)) @property def field_type(self): @@ -1226,12 +1274,6 @@ class PinnedArray(PinnedType): else: return self._field_type.get(self._vm, self.index2addr(idx)) - def deref_get(self, idx): - """If self.field_type is a Ptr, return the PinnedType self[idx] points - to. - """ - return self._field_type.deref_get(self._vm, self[idx]) - def __setitem__(self, idx, item): if isinstance(idx, slice): idx = self._normalize_slice(idx) @@ -1243,12 +1285,6 @@ class PinnedArray(PinnedType): else: self._field_type.set(self._vm, self.index2addr(idx), item) - def deref_set(self, idx, item): - """If self.field_type is a Ptr, set the PinnedType self[idx] points - to with @item. - """ - self._field_type.deref_set(self._vm, self[idx], item) - # just a shorthand def as_mem_str(self, encoding="ansi"): return self.cast(PinnedStr, encoding) diff --git a/test/analysis/mem.py b/test/analysis/mem.py index 5c0388f3..dca3346b 100644 --- a/test/analysis/mem.py +++ b/test/analysis/mem.py @@ -7,9 +7,9 @@ import struct from miasm2.analysis.machine import Machine from miasm2.analysis.mem import PinnedStruct, Num, Ptr, PinnedStr, PinnedArray,\ PinnedSizedArray, Array, mem_array_type,\ - mem_sized_array_type, RawStruct, Inline, pin,\ + mem_sized_array_type, RawStruct, pin,\ Union, BitField, PinnedSelf, PinnedVoid, Bits, \ - set_allocator + set_allocator, PinnedUnion, Struct from miasm2.jitter.csts import PAGE_READ, PAGE_WRITE from miasm2.os_dep.common import heap @@ -24,8 +24,7 @@ class MyStruct(PinnedStruct): # Number field: just struct.pack fields with one value ("num", Num("I")), ("flags", Num("B")), - # Ptr fields are Num, but they can also be dereferenced - # (self.deref_<field>). Deref can be read and set. + # TODO: comment ("other", Ptr("I", OtherStruct)), # Ptr to a variable length String ("s", Ptr("I", PinnedStr)), @@ -61,15 +60,15 @@ assert memval == 3 mstruct.memset() assert mstruct.num == 0 assert mstruct.flags == 0 -assert mstruct.other == 0 -assert mstruct.s == 0 -assert mstruct.i == 0 +assert mstruct.other.val == 0 +assert mstruct.s.val == 0 +assert mstruct.i.val == 0 mstruct.memset('\x11') assert mstruct.num == 0x11111111 assert mstruct.flags == 0x11 -assert mstruct.other == 0x11111111 -assert mstruct.s == 0x11111111 -assert mstruct.i == 0x11111111 +assert mstruct.other.val == 0x11111111 +assert mstruct.s.val == 0x11111111 +assert mstruct.i.val == 0x11111111 # From now, just use heap.vm_alloc @@ -85,19 +84,21 @@ other.foo = 0x1234 assert other.foo == 0x1234 ## Basic usage -mstruct.other = other.get_addr() -assert mstruct.other == other.get_addr() -assert mstruct.deref_other == other -assert mstruct.deref_other.foo == 0x1234 +mstruct.other.val = other.get_addr() +# This also works for now: +# mstruct.other = other.get_addr() +assert mstruct.other.val == other.get_addr() +assert mstruct.other.deref == other +assert mstruct.other.deref.foo == 0x1234 ## Deref assignment other2 = OtherStruct(jitter.vm) other2.foo = 0xbeef -assert mstruct.deref_other != other2 -mstruct.deref_other = other2 -assert mstruct.deref_other == other2 -assert mstruct.deref_other.foo == 0xbeef -assert mstruct.other == other.get_addr() # Addr did not change +assert mstruct.other.deref != other2 +mstruct.other.deref = other2 +assert mstruct.other.deref == other2 +assert mstruct.other.deref.foo == 0xbeef +assert mstruct.other.val == other.get_addr() # Addr did not change assert other.foo == 0xbeef # Deref assignment copies by value assert other2.foo == 0xbeef assert other.get_addr() != other2.get_addr() # Not the same address @@ -105,11 +106,12 @@ assert other == other2 # But same value ## Same stuff for Ptr to PinnedField alloc_addr = my_heap.vm_alloc(jitter.vm, - mstruct.get_field_type("i").dst_type.sizeof()) + mstruct.get_type().get_field_type("i") + .dst_type.sizeof()) mstruct.i = alloc_addr -mstruct.deref_i.value = 8 -assert mstruct.deref_i.value == 8 -assert mstruct.i == alloc_addr +mstruct.i.deref.val = 8 +assert mstruct.i.deref.val == 8 +assert mstruct.i.val == alloc_addr memval = struct.unpack("I", jitter.vm.get_mem(alloc_addr, 4))[0] assert memval == 8 @@ -117,37 +119,37 @@ assert memval == 8 # Str tests ## Basic tests memstr = PinnedStr(jitter.vm, addr_str) -memstr.value = "" -assert memstr.value == "" +memstr.val = "" +assert memstr.val == "" assert jitter.vm.get_mem(memstr.get_addr(), 1) == '\x00' -memstr.value = "lala" +memstr.val = "lala" assert jitter.vm.get_mem(memstr.get_addr(), memstr.get_size()) == 'lala\x00' jitter.vm.set_mem(memstr.get_addr(), 'MIAMs\x00') -assert memstr.value == 'MIAMs' +assert memstr.val == 'MIAMs' ## Ptr(PinnedStr) manipulations -mstruct.s = memstr.get_addr() -assert mstruct.s == addr_str -assert mstruct.deref_s == memstr -assert mstruct.deref_s.value == 'MIAMs' -mstruct.deref_s.value = "That's all folks!" -assert mstruct.deref_s.value == "That's all folks!" -assert memstr.value == "That's all folks!" +mstruct.s.val = memstr.get_addr() +assert mstruct.s.val == addr_str +assert mstruct.s.deref == memstr +assert mstruct.s.deref.val == 'MIAMs' +mstruct.s.deref.val = "That's all folks!" +assert mstruct.s.deref.val == "That's all folks!" +assert memstr.val == "That's all folks!" ## Other address, same value, same encoding memstr2 = PinnedStr(jitter.vm, addr_str2) -memstr2.value = "That's all folks!" +memstr2.val = "That's all folks!" assert memstr2.get_addr() != memstr.get_addr() assert memstr2 == memstr ## Same value, other encoding memstr3 = PinnedStr(jitter.vm, addr_str3, "utf16") -memstr3.value = "That's all folks!" +memstr3.val = "That's all folks!" assert memstr3.get_addr() != memstr.get_addr() assert memstr3.get_size() != memstr.get_size() # Size is different assert str(memstr3) != str(memstr) # Pinned representation is different assert memstr3 != memstr # Encoding is different, so they are not eq -assert memstr3.value == memstr.value # But the python value is the same +assert memstr3.val == memstr.val # But the python value is the same # PinnedArray tests @@ -254,7 +256,7 @@ for val in ms2.s2: assert val == 2 -# Inline tests +# Inlining a PinnedType tests class InStruct(PinnedStruct): fields = [ ("foo", Num("B")), @@ -264,7 +266,7 @@ class InStruct(PinnedStruct): class ContStruct(PinnedStruct): fields = [ ("one", Num("B")), - ("instruct", Inline(InStruct)), + ("instruct", InStruct.get_type()), ("last", Num("B")), ] @@ -298,7 +300,7 @@ class UniStruct(PinnedStruct): fields = [ ("one", Num("B")), ("union", Union([ - ("instruct", Inline(InStruct)), + ("instruct", InStruct.get_type()), ("i", Num(">I")), ])), ("last", Num("B")), @@ -308,20 +310,21 @@ uni = UniStruct(jitter.vm) jitter.vm.set_mem(uni.get_addr(), ''.join(chr(x) for x in xrange(len(uni)))) assert len(uni) == 6 # 1 + max(InStruct.sizeof(), 4) + 1 assert uni.one == 0x00 -assert uni.instruct.foo == 0x01 -assert uni.instruct.bar == 0x02 -assert uni.i == 0x01020304 +assert uni.union.instruct.foo == 0x01 +assert uni.union.instruct.bar == 0x02 +assert uni.union.i == 0x01020304 assert uni.last == 0x05 -uni.instruct.foo = 0x02 -assert uni.i == 0x02020304 -uni.i = 0x11223344 -assert uni.instruct.foo == 0x11 -assert uni.instruct.bar == 0x22 +uni.union.instruct.foo = 0x02 +assert uni.union.i == 0x02020304 +uni.union.i = 0x11223344 +assert uni.union.instruct.foo == 0x11 +assert uni.union.instruct.bar == 0x22 # BitField test -class BitStruct(PinnedStruct): +class BitStruct(PinnedUnion): fields = [ + ("flags_num", Num("H")), ("flags", BitField(Num("H"), [ ("f1_1", 1), ("f2_5", 5), @@ -332,24 +335,24 @@ class BitStruct(PinnedStruct): bit = BitStruct(jitter.vm) bit.memset() -assert bit.flags == 0 -assert bit.f1_1 == 0 -assert bit.f2_5 == 0 -assert bit.f3_8 == 0 -assert bit.f4_1 == 0 -bit.f1_1 = 1 -bit.f2_5 = 0b10101 -bit.f3_8 = 0b10000001 -assert bit.flags == 0b0010000001101011 -assert bit.f1_1 == 1 -assert bit.f2_5 == 0b10101 -assert bit.f3_8 == 0b10000001 -assert bit.f4_1 == 0 -bit.flags = 0b1101010101011100 -assert bit.f1_1 == 0 -assert bit.f2_5 == 0b01110 -assert bit.f3_8 == 0b01010101 -assert bit.f4_1 == 1 +assert bit.flags_num == 0 +assert bit.flags.f1_1 == 0 +assert bit.flags.f2_5 == 0 +assert bit.flags.f3_8 == 0 +assert bit.flags.f4_1 == 0 +bit.flags.f1_1 = 1 +bit.flags.f2_5 = 0b10101 +bit.flags.f3_8 = 0b10000001 +assert bit.flags_num == 0b0010000001101011 +assert bit.flags.f1_1 == 1 +assert bit.flags.f2_5 == 0b10101 +assert bit.flags.f3_8 == 0b10000001 +assert bit.flags.f4_1 == 0 +bit.flags_num = 0b1101010101011100 +assert bit.flags.f1_1 == 0 +assert bit.flags.f2_5 == 0b01110 +assert bit.flags.f3_8 == 0b01010101 +assert bit.flags.f4_1 == 1 # Unhealthy ideas @@ -363,72 +366,83 @@ class UnhealthyIdeas(PinnedStruct): ("pppself", Ptr("I", Ptr("I", Ptr("I", PinnedSelf)))), ] -# Other way to handle self dependency and circular dependencies -# NOTE: in this case, PinnedSelf would have been fine -UnhealthyIdeas.fields.append( - ("pppself2", Ptr("I", Ptr("I", Ptr("I", UnhealthyIdeas))))) -# Regen all fields -UnhealthyIdeas.gen_fields() - p_size = Ptr("I", PinnedVoid).size() ideas = UnhealthyIdeas(jitter.vm) ideas.memset() ideas.pself = ideas.get_addr() -assert ideas == ideas.deref_pself +assert ideas == ideas.pself.deref ideas.apself[0] = ideas.get_addr() -assert ideas.apself.deref_get(0) == ideas +assert ideas.apself[0].deref == ideas ideas.apself[1] = my_heap.vm_alloc(jitter.vm, UnhealthyIdeas.sizeof()) -ideas.apself.deref_set(1, ideas) +ideas.apself[1].deref = ideas assert ideas.apself[1] != ideas.get_addr() -assert ideas.apself.deref_get(1) == ideas +assert ideas.apself[1].deref == ideas ideas.ppself = my_heap.vm_alloc(jitter.vm, p_size) -ideas.deref_ppself.value = ideas.get_addr() -assert ideas.deref_ppself.value == ideas.get_addr() -assert ideas.deref_ppself.deref_value == ideas +ideas.ppself.deref.val = ideas.get_addr() +assert ideas.ppself.deref.val == ideas.get_addr() +assert ideas.ppself.deref.deref == ideas -ideas.deref_ppself.value = my_heap.vm_alloc(jitter.vm, UnhealthyIdeas.sizeof()) -ideas.deref_ppself.deref_value = ideas -assert ideas.deref_ppself.value != ideas.get_addr() -assert ideas.deref_ppself.deref_value == ideas +ideas.ppself.deref.val = my_heap.vm_alloc(jitter.vm, UnhealthyIdeas.sizeof()) +ideas.ppself.deref.deref = ideas +assert ideas.ppself.deref.val != ideas.get_addr() +assert ideas.ppself.deref.deref == ideas ideas.pppself = my_heap.vm_alloc(jitter.vm, p_size) -ideas.deref_pppself.value = my_heap.vm_alloc(jitter.vm, p_size) -ideas.deref_pppself.deref_value.value = ideas.get_addr() -assert ideas.deref_pppself.deref_value.deref_value == ideas +ideas.pppself.deref.val = my_heap.vm_alloc(jitter.vm, p_size) +ideas.pppself.deref.deref.val = ideas.get_addr() +assert ideas.pppself.deref.deref.deref == ideas + + +# Circular dependencies +class A(PinnedStruct): + pass + +class B(PinnedStruct): + fields = [("a", Ptr("I", A)),] + +# Gen A's fields after declaration +A.gen_fields([("b", Ptr("I", B)),]) + +a = A(jitter.vm) +b = B(jitter.vm) +a.b.val = b.get_addr() +b.a.val = a.get_addr() +assert a.b.deref == b +assert b.a.deref == a # Cast tests # PinnedStruct cast -PinnedInt = pin(Num("I")) -PinnedShort = pin(Num("H")) +PinnedInt = Num("I").pinned +PinnedShort = Num("H").pinned dword = PinnedInt(jitter.vm) -dword.value = 0x12345678 +dword.val = 0x12345678 assert isinstance(dword.cast(PinnedShort), PinnedShort) -assert dword.cast(PinnedShort).value == 0x5678 +assert dword.cast(PinnedShort).val == 0x5678 # Field cast ms2.s2[0] = 0x34 ms2.s2[1] = 0x12 -assert ms2.cast_field("s2", PinnedShort).value == 0x1234 +assert ms2.cast_field("s2", PinnedShort).val == 0x1234 # Other method -assert PinnedShort(jitter.vm, ms2.get_addr("s2")).value == 0x1234 +assert PinnedShort(jitter.vm, ms2.get_addr("s2")).val == 0x1234 # Manual cast inside an Array ms2.s2[4] = 0xcd ms2.s2[5] = 0xab -assert PinnedShort(jitter.vm, ms2.s2.index2addr(4)).value == 0xabcd +assert PinnedShort(jitter.vm, ms2.s2.index2addr(4)).val == 0xabcd # void* style cast -PinnedPtrVoid = pin(Ptr("I", PinnedVoid)) -PinnedPtrMyStruct = pin(Ptr("I", MyStruct)) +PinnedPtrVoid = Ptr("I", PinnedVoid).pinned +PinnedPtrMyStruct = Ptr("I", MyStruct).pinned p = PinnedPtrVoid(jitter.vm) -p.value = mstruct.get_addr() -assert p.deref_value.cast(MyStruct) == mstruct -assert p.cast(PinnedPtrMyStruct).deref_value == mstruct +p.val = mstruct.get_addr() +assert p.deref.cast(MyStruct) == mstruct +assert p.cast(PinnedPtrMyStruct).deref == mstruct # Field equality tests assert RawStruct("IH") == RawStruct("IH") @@ -438,11 +452,19 @@ assert Num(">I") != Num("<I") assert Ptr("I", MyStruct) == Ptr("I", MyStruct) assert Ptr(">I", MyStruct) != Ptr("<I", MyStruct) assert Ptr("I", MyStruct) != Ptr("I", MyStruct2) -assert Inline(MyStruct) == Inline(MyStruct) -assert Inline(MyStruct) != Inline(MyStruct2) +assert MyStruct.get_type() == MyStruct.get_type() +assert MyStruct.get_type() != MyStruct2.get_type() assert Array(Num("H"), 12) == Array(Num("H"), 12) assert Array(Num("H"), 11) != Array(Num("H"), 12) assert Array(Num("I"), 12) != Array(Num("H"), 12) +assert Struct("a", [("f1", Num("B")), ("f2", Num("H"))]) == \ + Struct("a", [("f1", Num("B")), ("f2", Num("H"))]) +assert Struct("a", [("f2", Num("B")), ("f2", Num("H"))]) != \ + Struct("a", [("f1", Num("B")), ("f2", Num("H"))]) +assert Struct("a", [("f1", Num("B")), ("f2", Num("H"))]) != \ + Struct("a", [("f1", Num("I")), ("f2", Num("H"))]) +assert Struct("a", [("f1", Num("B")), ("f2", Num("H"))]) != \ + Struct("b", [("f1", Num("B")), ("f2", Num("H"))]) assert Union([("f1", Num("B")), ("f2", Num("H"))]) == \ Union([("f1", Num("B")), ("f2", Num("H"))]) assert Union([("f2", Num("B")), ("f2", Num("H"))]) != \ @@ -463,17 +485,17 @@ assert BitField(Num("B"), [("f1", 1), ("f2", 4), ("f3", 1)]) != \ BitField(Num("B"), [("f1", 2), ("f2", 4), ("f3", 1)]) -# Quick pin(PinnedField)/PinnedField hash test: -assert pin(Num("f"))(jitter.vm, addr) == pin(Num("f"))(jitter.vm, addr) +# Quick PinnedField.pinned/PinnedField hash test +assert Num("f").pinned(jitter.vm, addr) == Num("f").pinned(jitter.vm, addr) # Types are cached -assert pin(Num("f")) == pin(Num("f")) -assert pin(Num("d")) != pin(Num("f")) -assert pin(Union([("f1", Num("I")), ("f2", Num("H"))])) == \ - pin(Union([("f1", Num("I")), ("f2", Num("H"))])) +assert Num("f").pinned == Num("f").pinned +assert Num("d").pinned != Num("f").pinned +assert Union([("f1", Num("I")), ("f2", Num("H"))]).pinned == \ + Union([("f1", Num("I")), ("f2", Num("H"))]).pinned assert mem_array_type(Num("B")) == mem_array_type(Num("B")) assert mem_array_type(Num("I")) != mem_array_type(Num("B")) -assert mem_sized_array_type(Num("B"), 20) == mem_sized_array_type(Num("B"), 20) -assert mem_sized_array_type(Num("B"), 19) != mem_sized_array_type(Num("B"), 20) +assert Array(Num("B"), 20).pinned == Array(Num("B"), 20).pinned +assert Array(Num("B"), 19).pinned != Array(Num("B"), 20).pinned # Repr tests @@ -485,8 +507,8 @@ print repr(cont), '\n' print repr(uni), '\n' print repr(bit), '\n' print repr(ideas), '\n' -print repr(pin(Array(Inline(MyStruct2), 2))(jitter.vm, addr)), '\n' -print repr(pin(Num("f"))(jitter.vm, addr)), '\n' +print repr(Array(MyStruct2.get_type(), 2).pinned(jitter.vm, addr)), '\n' +print repr(Num("f").pinned(jitter.vm, addr)), '\n' print repr(memarray) print repr(memsarray) print repr(memstr) |