#!/usr/bin/env python # miasm2.analysis.mem tests import struct from miasm2.analysis.machine import Machine from miasm2.analysis.mem import PinnedStruct, Num, Ptr, PinnedStr, PinnedArray,\ PinnedSizedArray, Array, mem_array_type,\ RawStruct, Union, BitField, PinnedSelf, \ PinnedVoid, Bits, set_allocator, PinnedUnion, \ Struct from miasm2.jitter.csts import PAGE_READ, PAGE_WRITE from miasm2.os_dep.common import heap # Two structures with some fields class OtherStruct(PinnedStruct): fields = [ ("foo", Num("H")), ] class MyStruct(PinnedStruct): fields = [ # Number field: just struct.pack fields with one value ("num", Num("I")), ("flags", Num("B")), # TODO: comment ("other", Ptr("I", OtherStruct)), # Ptr to a variable length String ("s", Ptr("I", PinnedStr)), ("i", Ptr("I", Num("I"))), ] jitter = Machine("x86_32").jitter("python") jitter.init_stack() addr = 0x1000 size = 0x1000 addr_str = 0x1100 addr_str2 = 0x1200 addr_str3 = 0x1300 # Initialize all mem with 0xaa jitter.vm.add_memory_page(addr, PAGE_READ | PAGE_WRITE, "\xaa"*size) # PinnedStruct tests ## Creation # Use manual allocation with explicit addr for the first example mstruct = MyStruct(jitter.vm, addr) ## Fields are read from the virtual memory assert mstruct.num == 0xaaaaaaaa assert mstruct.flags == 0xaa ## Field assignment modifies virtual memory mstruct.num = 3 assert mstruct.num == 3 memval = struct.unpack("I", jitter.vm.get_mem(mstruct.get_addr(), 4))[0] assert memval == 3 ## Pinnedset sets the whole structure mstruct.memset() assert mstruct.num == 0 assert mstruct.flags == 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.val == 0x11111111 assert mstruct.s.val == 0x11111111 assert mstruct.i.val == 0x11111111 # From now, just use heap.vm_alloc my_heap = heap() set_allocator(my_heap.vm_alloc) # Ptr tests ## Setup for Ptr tests # the addr field can now be omited since allocator is set other = OtherStruct(jitter.vm) other.foo = 0x1234 assert other.foo == 0x1234 ## Basic usage 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.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 assert other == other2 # But same value ## Same stuff for Ptr to PinnedField alloc_addr = my_heap.vm_alloc(jitter.vm, mstruct.get_type().get_field_type("i") .dst_type.sizeof()) 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 # Str tests ## Basic tests memstr = PinnedStr(jitter.vm, addr_str) memstr.val = "" assert memstr.val == "" assert jitter.vm.get_mem(memstr.get_addr(), 1) == '\x00' 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.val == 'MIAMs' ## Ptr(PinnedStr) manipulations 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.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.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.val == memstr.val # But the python value is the same # PinnedArray tests # Allocate buffer manually, since memarray is unsized alloc_addr = my_heap.vm_alloc(jitter.vm, 0x100) memarray = PinnedArray(jitter.vm, alloc_addr, Num("I")) # This also works: _memarray = mem_array_type(Num("I"))(jitter.vm, alloc_addr) memarray[0] = 0x02 assert memarray[0] == 0x02 assert jitter.vm.get_mem(memarray.get_addr(), Num("I").size()) == '\x02\x00\x00\x00' memarray[2] = 0xbbbbbbbb assert memarray[2] == 0xbbbbbbbb assert jitter.vm.get_mem(memarray.get_addr() + 2 * Num("I").size(), Num("I").size()) == '\xbb\xbb\xbb\xbb' try: s = str(memarray) assert False, "Should raise" except (NotImplementedError, ValueError): pass try: s = len(memarray) assert False, "Should raise" except (NotImplementedError, ValueError): pass ## Slice assignment memarray[2:4] = [3, 3] assert memarray[2] == 3 assert memarray[3] == 3 assert memarray[2:4] == [3, 3] try: memarray[2:4] = [3, 3, 3] assert False, "Should raise, mismatched sizes" except ValueError: pass try: memarray[1, 2] assert False, "Should raise, mismatched sizes" except ValueError: pass # PinnedSizedArray tests memsarray = PinnedSizedArray(jitter.vm, None, Num("I"), 10) # This also works: _memsarray = Array(Num("I"), 10).pinned(jitter.vm) # And Array(type, size).pinned generates statically sized types assert _memsarray.sizeof() == len(memsarray) memsarray.memset('\xcc') assert memsarray[0] == 0xcccccccc assert len(memsarray) == 10 * 4 assert str(memsarray) == '\xcc' * (4 * 10) for val in memsarray: assert val == 0xcccccccc assert list(memsarray) == [0xcccccccc] * 10 memsarray[0] = 2 assert memsarray[0] == 2 assert str(memsarray) == '\x02\x00\x00\x00' + '\xcc' * (4 * 9) # Atypical fields (RawStruct and Array) class MyStruct2(PinnedStruct): fields = [ ("s1", RawStruct("=BI")), ("s2", Array(Num("B"), 10)), ] ms2 = MyStruct2(jitter.vm) ms2.memset('\xaa') assert len(ms2) == 15 ## RawStruct assert len(ms2.s1) == 2 assert ms2.s1[0] == 0xaa assert ms2.s1[1] == 0xaaaaaaaa ## Array ### Basic checks assert len(ms2.s2) == 10 for val in ms2.s2: assert val == 0xaa assert ms2.s2[0] == 0xaa assert ms2.s2[9] == 0xaa ### Subscript assignment ms2.s2[3] = 2 assert ms2.s2[3] == 2 ### Field assignment (list) ms2.s2 = [1] * 10 for val in ms2.s2: assert val == 1 ### Field assignment (PinnedSizedArray) array2 = PinnedSizedArray(jitter.vm, None, Num("B"), 10) jitter.vm.set_mem(array2.get_addr(), '\x02'*10) for val in array2: assert val == 2 ms2.s2 = array2 for val in ms2.s2: assert val == 2 # Inlining a PinnedType tests class InStruct(PinnedStruct): fields = [ ("foo", Num("B")), ("bar", Num("B")), ] class ContStruct(PinnedStruct): fields = [ ("one", Num("B")), ("instruct", InStruct.get_type()), ("last", Num("B")), ] cont = ContStruct(jitter.vm) cont.memset() assert len(cont) == 4 assert len(cont.instruct) == 2 assert cont.one == 0 assert cont.last == 0 assert cont.instruct.foo == 0 assert cont.instruct.bar == 0 cont.memset('\x11') assert cont.one == 0x11 assert cont.last == 0x11 assert cont.instruct.foo == 0x11 assert cont.instruct.bar == 0x11 cont.one = 0x01 cont.instruct.foo = 0x02 cont.instruct.bar = 0x03 cont.last = 0x04 assert cont.one == 0x01 assert cont.instruct.foo == 0x02 assert cont.instruct.bar == 0x03 assert cont.last == 0x04 assert jitter.vm.get_mem(cont.get_addr(), len(cont)) == '\x01\x02\x03\x04' # Union test class UniStruct(PinnedStruct): fields = [ ("one", Num("B")), ("union", Union([ ("instruct", InStruct.get_type()), ("i", Num(">I")), ])), ("last", Num("B")), ] 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.union.instruct.foo == 0x01 assert uni.union.instruct.bar == 0x02 assert uni.union.i == 0x01020304 assert uni.last == 0x05 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(PinnedUnion): fields = [ ("flags_num", Num("H")), ("flags", BitField(Num("H"), [ ("f1_1", 1), ("f2_5", 5), ("f3_8", 8), ("f4_1", 1), ])), ] bit = BitStruct(jitter.vm) bit.memset() 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 class UnhealthyIdeas(PinnedStruct): fields = [ ("pastruct", Ptr("I", PinnedArray, RawStruct("=Bf"))), ("apstr", Array(Ptr("I", PinnedStr), 10)), ("pself", Ptr("I", PinnedSelf)), ("apself", Array(Ptr("I", PinnedSelf), 2)), ("ppself", Ptr("I", Ptr("I", PinnedSelf))), ("pppself", Ptr("I", Ptr("I", Ptr("I", PinnedSelf)))), ] p_size = Ptr("I", PinnedVoid).size() ideas = UnhealthyIdeas(jitter.vm) ideas.memset() ideas.pself = ideas.get_addr() assert ideas == ideas.pself.deref ideas.apself[0] = ideas.get_addr() assert ideas.apself[0].deref == ideas ideas.apself[1] = my_heap.vm_alloc(jitter.vm, UnhealthyIdeas.sizeof()) ideas.apself[1].deref = ideas assert ideas.apself[1] != ideas.get_addr() assert ideas.apself[1].deref == ideas ideas.ppself = my_heap.vm_alloc(jitter.vm, p_size) ideas.ppself.deref.val = ideas.get_addr() assert ideas.ppself.deref.val == ideas.get_addr() assert ideas.ppself.deref.deref == 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.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 = Num("I").pinned PinnedShort = Num("H").pinned dword = PinnedInt(jitter.vm) dword.val = 0x12345678 assert isinstance(dword.cast(PinnedShort), PinnedShort) assert dword.cast(PinnedShort).val == 0x5678 # Field cast ms2.s2[0] = 0x34 ms2.s2[1] = 0x12 assert ms2.cast_field("s2", PinnedShort).val == 0x1234 # Other method 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)).val == 0xabcd # void* style cast PinnedPtrVoid = Ptr("I", PinnedVoid).pinned PinnedPtrMyStruct = Ptr("I", MyStruct).pinned p = PinnedPtrVoid(jitter.vm) 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") assert RawStruct("I") != RawStruct("IH") assert Num("I") == Num("I") assert Num(">I") != Num("I", MyStruct) != Ptr("