-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathutils.py
More file actions
254 lines (220 loc) · 9.07 KB
/
utils.py
File metadata and controls
254 lines (220 loc) · 9.07 KB
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Loic Jaquemet loic.jaquemet+python@gmail.com
#
from past.builtins import long
import ctypes
import logging
import struct
from struct import pack
import os
from haystack.abc import interfaces
from haystack import types
"""This module holds several useful function helpers"""
__author__ = "Loic Jaquemet loic.jaquemet+python@gmail.com"
# never use ctypes import
log = logging.getLogger('utils')
class Utils(interfaces.ICTypesUtils):
def __init__(self, _target_ctypes):
self._ctypes = _target_ctypes
assert isinstance(_target_ctypes, types.CTypesProxy)
self.__local_process_memory_handler = None
def formatAddress(self, addr):
if self._ctypes.sizeof(self._ctypes.c_void_p) == 8:
return '0x%016x' % addr
else:
return '0x%08x' % addr
def unpackWord(self, bytes, endianess='@'):
if self._ctypes.sizeof(self._ctypes.c_void_p) == 8:
return struct.unpack('%sQ' % endianess, bytes)[0]
else:
return struct.unpack('%sI' % endianess, bytes)[0]
def is_address_local(self, obj, structType=None):
"""
Costly , checks if obj is mapped to local memory space.
Returns the memory mapping if found.
False, otherwise.
"""
addr = self.get_pointee_address(obj)
log.debug('get_pointee_address returned %x',addr)
if addr == 0:
return False
# maintain a cache to improve performance.
# if not found in cache, try to reload local process memory space.
# the pointer memory space could have been allocated recently.
# the calling function is most certainly going to fail anyway
if self.__local_process_memory_handler is not None:
ret = self.__local_process_memory_handler.is_valid_address(obj, structType)
if ret:
return ret
# loading dependencies
from haystack.mappings.process import make_local_memory_handler
memory_handler = make_local_memory_handler()
self.__local_process_memory_handler = memory_handler
return self.__local_process_memory_handler.is_valid_address(obj, structType)
def get_pointee_address(self, obj):
"""
Returns the address of the struct pointed by the obj, or null if invalid.
:param obj: a pointer.
"""
# check for homebrew POINTER
if hasattr(obj, '_sub_addr_'):
if callable(obj._sub_addr_):
log.debug('obj._sub_addr_: 0x%x', obj._sub_addr_())
return obj._sub_addr_()
log.debug('obj._sub_addr_: 0x%x', obj._sub_addr_)
return obj._sub_addr_
elif isinstance(obj, int) or isinstance(obj, long):
# basictype pointers are created as int.
return obj
elif not bool(obj):
return 0
elif self._ctypes.is_function_type(type(obj)):
return self._ctypes.cast(obj, self._ctypes.c_void_p).value
elif self._ctypes.is_pointer_type(type(obj)):
return self._ctypes.cast(obj, self._ctypes.c_void_p).value
# check for null pointers
# if bool(obj):
# FIXME unreachable
if not hasattr(obj, 'contents'):
return 0
# print '** NOT MY HAYSTACK POINTER'
return self.__ctypes.addressof(obj.contents)
else:
return 0
def container_of(self, memberaddr, typ, membername):
"""
From a pointer to a member, returns the parent struct.
Returns the instance of typ(), in which the member "membername' is really.
Useful in some Kernel linked list which used members as prec,next pointers.
:param memberadd: the address of membername.
:param typ: the type of the containing structure.
:param membername: the membername.
Stolen from linux kernel headers.
const typeof( ((typ *)0)->member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type,member) );})
"""
return typ.from_address(memberaddr - self.offsetof(typ, membername))
def offsetof(self, typ, membername):
"""
Returns the offset of a member in a structure.
:param typ: the structure type.
:param membername: the membername in that structure.
"""
return getattr(typ, membername).offset
def ctypes_to_python_array(self, array):
"""Converts an array of undetermined Basic self.__ctypes class to a python array,
by guessing it's type from it's class name.
This is a bad example of introspection.
"""
if isinstance(array, str) or isinstance(array, bytes):
# special case for c_char[]
return array
if not self._ctypes.is_array_of_basic_instance(array):
raise TypeError('NOT-AN-Basic-Type-ARRAY')
if array._type_ in [self._ctypes.c_int, self._ctypes.c_uint, self._ctypes.c_long,
self._ctypes.c_ulong, self._ctypes.c_ubyte, self._ctypes.c_byte]:
return [long(el) for el in array]
if array._type_ in [self._ctypes.c_float, self._ctypes.c_double, self._ctypes.c_longdouble]:
return [float(el) for el in array]
sb = ''.join([struct.pack(array._type_._type_, el) for el in array])
return sb
def array2bytes(self, array):
"""Converts an array of undetermined Basic self.__ctypes class to a byte string,
by guessing it's type from it's class name.
This is a bad example of introspection.
"""
if isinstance(array, str) or isinstance(array, bytes):
# special case for c_char[]
return array
if self._ctypes.is_array_of_basic_instance(array):
sb = b''.join([struct.pack(array._type_._type_, el) for el in array])
return sb
else:
c_size = self._ctypes.sizeof(array)
a2 = (self._ctypes.c_ubyte * c_size).from_address(self._ctypes.addressof(array))
sb = b''.join([struct.pack('B', el) for el in a2])
return sb
def bytes2array(self, bytes, typ):
"""
Converts a bytestring in a self.__ctypes array of typ() elements.
:param bytes: str
:param typ: ctypes
:return: array
"""
typLen = self._ctypes.sizeof(typ)
if len(bytes) % typLen != 0:
raise ValueError('thoses bytes are not an array of %s' % (typ))
arrayLen = len(bytes) // typLen
array = (typ * arrayLen)()
if arrayLen == 0:
return array
fmt = self._ctypes.get_pack_format()[typ.__name__]
try:
for i in range(0, arrayLen):
array[i] = struct.unpack(
fmt, bytes[typLen * i:typLen * (i + 1)])[0]
except struct.error as e:
log.error('format:%s typLen*i:typLen*(i+1) = %d:%d' %
(fmt, typLen * i, typLen * (i + 1)))
raise e
return array
def pointer2bytes(self, attr, nb_element):
"""
Returns an array from a self.__ctypes POINTER, given the number of elements.
:param attr: the structure member.
:param nb_element: the number of element in the array.
"""
# attr is a pointer and we want to read elementSize of type(attr.contents))
## DEBUG statement
# if not self.is_address_local(attr):
# raise TypeError('POINTER NOT LOCAL: %x', attr)
first_element_addr = self.get_pointee_address(attr)
array = (type(attr.contents) * nb_element).from_address(first_element_addr)
# we have an array type starting at attr.contents[0]
return self.array2bytes(array)
def get_subtype(self, cls):
"""get the subtype of a pointer, array or basic type with haystack quirks."""
# could use _pointer_type_cache
if hasattr(cls, '_subtype_'):
return cls._subtype_
return cls._type_
def get_word_size(self):
return self._ctypes.sizeof(self._ctypes.c_void_p)
try:
# Python 2
py_xrange = xrange
def xrange(start, end, step=1):
""" stoupid xrange can't handle long ints... """
end = end - start
for val in py_xrange(0, end, step):
yield start + val
return
except NameError as e:
# Python 3
xrange = range
def bytes2array(bytes, typ):
"""
Converts a bytestring in a ctypes array of typ() elements.
:param bytes: str
:param typ: ctypes
:return: array
"""
typLen = ctypes.sizeof(typ)
if len(bytes) % typLen != 0:
raise ValueError('thoses bytes are not an array of %s' % (typ))
arrayLen = len(bytes) // typLen
array = (typ * arrayLen)()
if arrayLen == 0:
return array
fmt = typ._type_
try:
for i in range(0, arrayLen):
array[i] = struct.unpack(
fmt, bytes[typLen * i:typLen * (i + 1)])[0]
except struct.error as e:
log.error('format:%s typLen*i:typLen*(i+1) = %d:%d' %
(fmt, typLen * i, typLen * (i + 1)))
raise e
return array