1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
|
#! /usr/bin/env python2
"""This script is just a short example of common usages for miasm2.core.types.
For a more complete view of what is possible, tests/core/types.py covers
most of the module possibilities, and the module doc gives useful information
as well.
"""
from miasm2.analysis.machine import Machine
from miasm2.core.types import MemStruct, Self, Void, Str, Array, Ptr, \
Num, Array, set_allocator
from miasm2.os_dep.common import heap
# Instantiate a heap
my_heap = heap()
# And set it as the default memory allocator, to avoid manual allocation and
# explicit address passing to the MemType subclasses (like MemStruct)
# constructor
set_allocator(my_heap.vm_alloc)
# Let's reimplement a simple C generic linked list mapped on a VmMngr.
# All the structures and methods will use the python objects but all the data
# is in fact stored in the VmMngr
class ListNode(MemStruct):
fields = [
# The "<I" is the struct-like format of the pointer in memory, in this
# case a Little Endian 32 bits unsigned int.
# One way to handle reference to ListNode in ListNode is to use the
# special marker Self().
# You could also generate ListNode's fields with ListNode.gen_field
# after the class declaration, so that the ListNode is defined when
# fields are generated.
("next", Ptr("<I", Self())),
# Ptr(_, Void()) is analogous to void*, Void() is a kind of "empty type"
("data", Ptr("<I", Void())),
]
def get_next(self):
if self.next.val == 0:
return None
return self.next.deref
def get_data(self, data_type=None):
if data_type is not None:
return self.data.deref.cast(data_type)
else:
return self.data.deref
class LinkedList(MemStruct):
fields = [
# For convenience, either a Type instance (like Self() or Num("I") or a
# MemStruct subclass can be passed to the Ptr constructor.
("head", Ptr("<I", ListNode)),
("tail", Ptr("<I", ListNode)),
# Num can take any one-field struct-like format, including floats and
# doubles
("size", Num("<I")),
]
def get_head(self):
"""Returns the head ListNode instance"""
if self.head == 0:
return None
return self.head.deref
def get_tail(self):
"""Returns the tail ListNode instance"""
if self.tail == 0:
return None
return self.tail.deref
def push(self, data):
"""Push a data (MemType instance) to the linked list."""
# Allocate a new node
node = ListNode(self._vm)
# Set the data pointer
node.data = data.get_addr()
# re-link
if self.head != 0:
# get the head ListNode
head = self.get_head()
node.next = head.get_addr()
# pointer to head assigned to the new node address
self.head = node.get_addr()
# Do not forget the tail :)
if self.tail == 0:
self.tail = node.get_addr()
self.size += 1
def pop(self, data_type=None):
"""Pop one data from the LinkedList."""
# Nothing to pop
if self.head == 0:
return None
node = self.get_head()
self.head = node.next
# empty
if self.head == 0:
self.tail = 0
self.size -= 1
return node.get_data(data_type)
def empty(self):
"""True if the list is empty."""
return self.head == 0
def __iter__(self):
if not self.empty():
cur = self.get_head()
while cur is not None:
yield cur.data.deref
cur = cur.get_next()
# Some data types to put in the LinkedList and play with:
class DataArray(MemStruct):
fields = [
("val1", Num("B")),
("val2", Num("B")),
# Ptr can also be instantiated with a Type instance as an argument, the
# corresponding Memtype will be returned when dereferencing
# Here, data_array.array.deref will allow to access an Array
("arrayptr", Ptr("<I", Array(Num("B"), 16))),
# Array of 10 uint8
("array", Array(Num("B"), 16)),
]
class DataStr(MemStruct):
fields = [
("valshort", Num("<H")),
# Pointer to an utf16 null terminated string
("data", Ptr("<I", Str("utf16"))),
]
print "This script demonstrates a LinkedList implementation using the types "
print "module in the first part, and how to play with some casts in the second."
print
# A random jitter
# You can also use miasm2.jitter.VmMngr.Vm(), but it does not happen in real
# life scripts, so here is the usual way:
jitter = Machine("x86_32").jitter("python")
vm = jitter.vm
# Auto-allocated by my_heap. If you allocate memory at `addr`,
# `link = LinkedList(vm, addr)` will use this allocation. If you just want
# to read/modify existing struct, you may want to use the (vm, addr) syntax.
link = LinkedList(vm)
# memset the struct (with '\x00' by default)
link.memset()
# Push three uninitialized structures
link.push(DataArray(vm))
link.push(DataArray(vm))
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_type()
.get_field_type("size").size)
assert raw_size == '\x03\x00\x00\x00'
print "The linked list just built:"
print repr(link), '\n'
print "Its uninitialized data elements:"
for data in link:
# __iter__ returns MemVoids here, just cast them to the real data type
real_data = data.cast(DataArray)
print repr(real_data)
print
# Now let's play with one data
data = link.pop(DataArray)
assert link.size == 2
# Make the Array Ptr point to the data's array field
# Note: this is equivalent to data.arrayptr.val = ...
data.arrayptr = data.get_addr("array")
# Now the pointer dereference is equal to the array field's value
assert data.arrayptr.deref == data.array
# Let's say that it is a DataStr:
datastr = data.cast(DataStr)
print "First element casted to DataStr:"
print repr(datastr)
print
# data and datastr really share the same memory:
data.val1 = 0x34
data.val2 = 0x12
assert datastr.valshort == 0x1234
datastr.valshort = 0x1122
assert data.val1 == 0x22 and data.val2 == 0x11
# Let's play with strings
memstr = datastr.data.deref
# Note that memstr is Str("utf16")
memstr.val = 'Miams'
print "Cast data.array to MemStr and set the string value:"
print repr(memstr)
print
# If you followed, memstr and data.array point to the same object, so:
raw_miams = '\x00'.join('Miams') + '\x00'*3
raw_miams_array = [ord(c) for c in raw_miams]
assert list(data.array)[:len(raw_miams_array)] == raw_miams_array
assert data.array.cast(Str("utf16")) == memstr
# Default is "ansi"
assert data.array.cast(Str()) != memstr
assert data.array.cast(Str("utf16")).val == memstr.val
print "See that the original array has been modified:"
print repr(data)
print
# Some type manipulation examples, for example let's construct an argv for
# a program:
# Let's say that we have two arguments, +1 for the program name and +1 for the
# final null ptr in argv, the array has 4 elements:
argv_t = Array(Ptr("<I", Str()), 4)
print "3 arguments argv type:", argv_t
# alloc argv somewhere
argv = argv_t.lval(vm)
# Auto alloc with the MemStr.from_str helper
MemStrAnsi = Str().lval
argv[0].val = MemStrAnsi.from_str(vm, "./my-program").get_addr()
argv[1].val = MemStrAnsi.from_str(vm, "arg1").get_addr()
argv[2].val = MemStrAnsi.from_str(vm, "27").get_addr()
argv[3].val = 0
# If you changed your mind on the second arg, you could do:
argv[2].deref.val = "42"
print "An argv instance:", repr(argv)
print "argv values:", repr([val.deref.val for val in argv[:-1]])
print
print "See test/core/types.py and the miasm2.core.types module doc for "
print "more information."
|