-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathbasicmodel.py
More file actions
595 lines (549 loc) · 27.7 KB
/
basicmodel.py
File metadata and controls
595 lines (549 loc) · 27.7 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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Loic Jaquemet loic.jaquemet+python@gmail.com
#
"""
This module is the main aspect of haystack.
This specific plugin handles basic types.
"""
import ctypes
import logging
from haystack import constraints
from haystack import utils
from haystack.abc import interfaces
__author__ = "Loic Jaquemet"
__copyright__ = "Copyright (C) 2012 Loic Jaquemet"
__email__ = "loic.jaquemet+python@gmail.com"
__license__ = "GPL"
__maintainer__ = "Loic Jaquemet"
__status__ = "Production"
log = logging.getLogger('basicmodel')
def get_field_type(record, fieldname):
""" return a members type"""
ret = [(n, fieldtype)
for n, fieldtype in get_fields(record) if n == fieldname]
if len(ret) != 1:
raise TypeError('No such field name %s in %s' % (fieldname, record))
return ret[0][1]
def get_fields(record):
if not isinstance(record, ctypes.Structure) and not isinstance(record, ctypes.Union):
raise TypeError('Feed me a ctypes record instance. Not: %s'% record)
return get_record_type_fields(type(record))
def get_record_type_fields(record_type):
if not issubclass(record_type, ctypes.Structure) and not issubclass(record_type, ctypes.Union):
raise TypeError('Feed me a ctypes record type')
mro = list(record_type.__mro__[:-3]) # cut Structure, _CData and object
mro.reverse()
me = mro.pop(-1)
for typ in mro: # firsts are first, cls is in here in [-1]
if not hasattr(typ, '_fields_'):
continue
for name, vtyp in get_fields(typ):
yield (name, vtyp)
# print mines.
for f in me._fields_:
yield (f[0], f[1])
#raise StopIteration
return
class CTypesRecordConstraintValidator(interfaces.IRecordConstraintsValidator):
"""
This is the main class, to be inherited by all ctypes record validators.
It adds a generic validation framework, based on simple assertion,
and on more complex constraint on members values.
FIXME: ConstraintsValidator should be loaded with a memory mapping, not an handler.
The target platform is different mapping by mapping. (windows heap 32/64)
"""
MAX_CSTRING_SIZE = 1024
def __init__(self, memory_handler, my_constraints, target_ctypes=None):
"""
:param memory_handler: IMemoryHandler
:param my_constraints: IModuleConstraints
:param target_ctypes: Ctypes module, could be a different arch.
:return:
"""
if not isinstance(memory_handler, interfaces.IMemoryHandler):
raise TypeError("Feed me a IMemoryHandler")
if my_constraints and not isinstance(my_constraints, interfaces.IModuleConstraints):
raise TypeError("Feed me a IModuleConstraints")
if target_ctypes is None:
target_ctypes = memory_handler.get_target_platform().get_target_ctypes()
if not hasattr(target_ctypes, 'c_ubyte'):
raise TypeError("Feed me a target_ctypes as Ctypes modules")
self._memory_handler = memory_handler
self._ctypes = target_ctypes
self._utils = utils.Utils(self._ctypes)
self._constraints_base = None
self._constraints_dynamic = None
if my_constraints is not None:
self._constraints_base = my_constraints.get_constraints()
self._constraints_dynamic = my_constraints.get_dynamic_constraints()
def _get_constraints_for(self, record):
n = record.__class__.__name__
if self._constraints_base and n in self._constraints_base:
return self._constraints_base[n]
return dict()
def _get_dynamic_constraints_for(self, record):
n = record.__class__.__name__
if self._constraints_dynamic and n in self._constraints_dynamic:
return self._constraints_dynamic[n]
return None
def get_orig_addr(self, record):
""" returns the vaddr of this instance."""
haystack_addr = self._ctypes.addressof(record)
# FIXME test
m = self._memory_handler.get_mapping_for_address(haystack_addr)
return m._ptov(haystack_addr)
def is_valid(self, record):
"""
Checks if each members has coherent data
For each Field, check on of the three case,
a) basic types (check for expectedValues),
if field as some expected values in expectedValues
check field value against expectedValues[fieldname]
if False, return False, else continue
b) struct(check isValid)
check if the inner struct isValid()
if False, return False, else continue
c) is an array , recurse validation
d) Pointer(check valid_address or expectedValues is None == NULL )
if field as some expected values in expectedValues
( None or 0 ) are the only valid options to design NULL pointers
check field get_pointee_address() value against expectedValues[fieldname] // if NULL
if True(address is NULL and it's a valid value), continue
check get_pointee_address against is_valid_address()
if False, return False, else continue
"""
# FIXME, for now its a debug statement
if not isinstance(record, ctypes.Structure) and not isinstance(record, ctypes.Union):
raise TypeError('Feed me a record')
valid = self._is_valid(record, self._get_constraints_for(record))
log.debug('-- <%s> isValid = %s', record.__class__.__name__, valid)
# check dynamic constraints last.
if valid:
return self._is_valid_dynamic_constraints(record)
return False
def _is_valid_dynamic_constraints(self, record):
dynamic_constraints = self._get_dynamic_constraints_for(record)
if dynamic_constraints:
log.debug("dynamic constraints are %s", dynamic_constraints)
return dynamic_constraints.is_valid(record)
return True
def _is_valid(self, record, record_constraints):
""" real implementation. check expectedValues first, then the other fields """
log.debug(' -- <%s> isValid --', record.__class__.__name__)
done = []
# we check constrained field first to stop early if possible
# then we test the other fields
log.debug("constraints are on %s", record_constraints)
_fieldsTuple = get_fields(record)
myfields = dict(_fieldsTuple)
for attrname, _constraints in record_constraints.items():
if attrname not in myfields:
log.warning('constraint check: field %s does not exists in record for %s',
attrname, record.__class__.__name__)
continue
done.append(attrname)
attrtype = myfields[attrname]
attr = getattr(record, attrname)
ignore = False
for expected in _constraints:
log.debug(' +++ %s %s ', attrname, expected)
if expected is constraints.IgnoreMember:
ignore = True
break
if ignore:
log.debug('IgnoreMember: %s ', attrname)
continue
if not self._is_valid_attr(attr, attrname, attrtype, record_constraints):
return False
# check the other fields for validation
todo = [(name, typ) for name, typ in get_fields(record) if name not in done]
for attrname, attrtype, in todo:
attr = getattr(record, attrname)
if not self._is_valid_attr(attr, attrname, attrtype, record_constraints):
return False
# validation done
return True
def _is_valid_attr(self, attr, attrname, attrtype, record_constraints):
""" Validation of a single member """
# a)
log.debug('valid: %s, %s' % (attrname, attrtype))
if self._ctypes.is_basic_type(attrtype):
if attrname in record_constraints:
if attr not in record_constraints.get_constraints_for_field(attrname):
log.debug(
'basicType: %s %s %s bad value not in record_constraints[attrname]:',
attrname, attrtype, repr(attr))
return False
log.debug('basicType: %s %s %s ok', attrname, attrtype, repr(attr))
return True
# b)
elif self._ctypes.is_struct_type(attrtype) or self._ctypes.is_union_type(attrtype):
# do i need to load it first ? because it should be memcopied with
# the super()..
if not self.is_valid(attr):
log.debug('structType: %s %s %s isValid FALSE', attrname, attrtype, repr(attr))
return False
log.debug('structType: %s %s %s isValid TRUE', attrname, attrtype, repr(attr))
return True
# c)
elif self._ctypes.is_array_of_basic_type(attrtype):
if attrname in record_constraints:
if attr not in record_constraints[attrname]:
log.debug(
'basicArray: %s %s %s - bad value not in record_constraints[attrname]:',
attrname, attrtype, type(attr))
return False
log.debug('basicArray: %s is arraytype %s we decided it was valid', attrname, type(attr))
return True
# d)
elif self._ctypes.is_array_type(attrtype):
log.debug('array: %s is arraytype %s recurse validate', attrname, repr(attr))
attrLen = len(attr)
if attrLen == 0:
return True
elType = type(attr[0])
for i in range(0, attrLen):
# FIXME BUG DOES NOT WORK - offsetof("%s[%d]") is called,
# and %s exists, not %s[%d]
if not self._is_valid_attr(attr[i], "%s[%d]" % (attrname, i), elType, record_constraints):
return False
return True
# e)
elif self._ctypes.is_cstring_type(attrtype):
myaddress = self._utils.get_pointee_address(attr.ptr)
if attrname in record_constraints:
# test if NULL is an option
if not bool(myaddress):
if not ((None in record_constraints[attrname]) or (0 in record_constraints[attrname])):
log.debug('str: %s %s %s isNULL - NOT EXPECTED', attrname, attrtype, repr(attr))
return False
log.debug('str: %s %s %s isNULL - OK', attrname, attrtype, repr(attr))
# e.1)
return True
if myaddress != 0 and not self.is_valid_address_value(myaddress):
log.debug('str: %s %s %s 0x%lx INVALID', attrname, attrtype, repr(attr), myaddress)
# e.2)
return False
log.debug('str: %s %s %s is at 0x%lx OK', attrname, attrtype, repr(attr), myaddress)
# e.3)
return True
# f)
elif self._ctypes.is_pointer_type(attrtype):
myaddress = self._utils.get_pointee_address(attr)
#log.debug('_is_valid_attr:0x%x name: %s', myaddress, attrname)
if attrname in record_constraints:
# test if NULL is an option
log.debug('self._ctypes.is_pointer_type: bool(attr):%s attr:%s', bool(attr), attr)
if not bool(myaddress):
if not ((None in record_constraints[attrname]) or (0 in record_constraints[attrname])):
log.debug('ptr: %s %s %s isNULL - NOT EXPECTED', attrname, attrtype, repr(attr))
# f.1) expectedValues specifies NULL to be invalid
return False
log.debug('ptr: %s %s %s isNULL - OK', attrname, attrtype, repr(attr))
# f.2) expectedValues specifies NULL to be valid
return True
_attrType = None
if self._ctypes.is_pointer_to_void_type(attrtype) or self._ctypes.is_function_type(attrtype):
log.debug('Its a simple type. Checking address only. attr=%s', attr)
if (myaddress != 0 and not self.is_valid_address_value(myaddress)):
log.debug('voidptr: %s %s %s 0x%lx INVALID simple pointer',
attrname, attrtype, repr(attr), myaddress)
# f.3) address must be valid, no type requirement
return False
else:
# test valid address mapping
_attrType = self._utils.get_subtype(attrtype)
if myaddress != 0 and not self.is_valid_address(attr, _attrType):
log.debug('ptr: %s %s %s 0x%lx INVALID', attrname, attrtype,
repr(attr), self._utils.get_pointee_address(attr))
# f.4) its a pointer, but not valid in our _memory_handler for this
# pointee type.
return False
log.debug('ptr: name:%s repr:%s address:0x%lx OK', attrname,
repr(attr), self._utils.get_pointee_address(attr))
# f.5) null is accepted by default
return True
# g)
log.error('What type are You ?: %s/%s' % (attrname, attrtype))
return True
def _is_loadable_member(self, attr, attrname, attrtype):
"""
Check if the member is loadable.
A c_void_p cannot be load generically, You have to take care of that.
(Pointers with valid address space value
AND (pointee is a struct type OR pointee is a union type)
) OR struct type OR union type
"""
return bool(attr) and not self._ctypes.is_pointer_to_void_type(attrtype)
def load_members(self, record, max_depth):
"""
The validity of the members will be assessed.
Each members that can be ( allocators, pointers), will be evaluated for
validity and loaded recursively.
:param record: the record to load
:param max_depth: limitation of depth after which the loading/validation
will stop and return results.
@returns True if everything has been loaded, False if something went
wrong.
"""
if max_depth <= 0:
log.debug('Maximum depth reach. Not loading any deeper members.')
log.debug('Struct partially LOADED. %s not loaded', record.__class__.__name__)
return True
if max_depth > 100:
raise RuntimeError('max_depth')
max_depth -= 1
if not self.is_valid(record):
return False
log.debug('- <%s> do load_members -', record.__class__.__name__)
# go through all members. if they are pointers AND not null AND in
# valid memorymapping AND a struct type, load them as struct pointers
record_constraints = self._get_constraints_for(record)
for attrname, attrtype in get_fields(record):
attr = getattr(record, attrname)
ignore = False
# shorcut ignores
if attrname in record_constraints:
for _constraint in record_constraints[attrname]:
# shortcut
if _constraint is constraints.IgnoreMember:
ignore = True
break
if ignore:
continue
try:
if not self._load_member(record, attr, attrname, attrtype, record_constraints, max_depth):
return False
except ValueError as e:
log.error('maxDepth was %d' % max_depth)
raise
log.debug('- <%s> END load_members -', record.__class__.__name__)
return True
def _load_member(self, record, attr, attrname, attrtype, record_constraints, max_depth):
# skip static void_p data members
if not self._is_loadable_member(attr, attrname, attrtype):
log.debug("%s %s not loadable bool(attr) = %s", attrname, attrtype, bool(attr))
return True
# load it, fields are valid
elif self._ctypes.is_struct_type(attrtype) or self._ctypes.is_union_type(attrtype):
# its an embedded record. Bytes are already loaded.
offset = self._utils.offsetof(type(record), attrname)
log.debug('st: %s %s is STRUCT at @%x', attrname, attrtype, record._orig_address_ + offset)
# TODO pydoc for impl.
attr._orig_address_ = record._orig_address_ + offset
if not self.load_members(attr, max_depth - 1):
log.debug("st: %s %s not valid, error while loading inner struct", attrname, attrtype)
return False
log.debug("st: %s %s inner struct LOADED ", attrname, attrtype)
return True
elif self._ctypes.is_array_of_basic_type(attrtype):
return True
elif self._ctypes.is_array_type(attrtype):
log.debug('a: %s is arraytype %s recurse load', attrname, repr(attr))
attrLen = len(attr)
if attrLen == 0:
return True
elType = type(attr[0])
for i in range(0, attrLen):
# FIXME BUG DOES NOT WORK
# offsetof("%s[%d]") is called, and %s exists, not %s[%d]
# if not self._load_member(attr[i], "%s[%d]"%(attrname,i),
# elType, _memory_handler, maxDepth):
if not self._load_member(record, attr[i], attrname, elType, record_constraints, max_depth):
return False
return True
# we have PointerType here . Basic or complex
# exception cases
elif self._ctypes.is_function_type(attrtype):
pass
# FIXME
elif self._ctypes.is_cstring_type(attrtype):
# can't use basic c_char_p because we can't load in foreign memory
# FIXME, you need to keep a ref to this ctring if
# your want _mappings_ to exists
# or just mandate _memory_handler in toString
attr_obj_address = self._utils.get_pointee_address(attr.ptr)
if not bool(attr_obj_address):
log.debug('%s %s is a CString, the pointer is null (validation '
'must have occurred earlier)', attrname, attr)
return True
memoryMap = self.is_valid_address_value(attr_obj_address)
if not memoryMap:
log.warning('Error on addr while fetching a CString.'
'should not happen')
return False
ref = self._memory_handler.getRef(self._ctypes.CString, attr_obj_address)
if ref is not None:
log.debug("%s %s loading from references cache %s/0x%lx", attrname,
attr, self._ctypes.CString, attr_obj_address)
return True
max_size = min(self.MAX_CSTRING_SIZE, memoryMap.end - attr_obj_address)
log.debug('%s %s is defined as a CString, loading %d bytes from 0x%lx '
'is_valid_address %s', attrname, attr, max_size, attr_obj_address,
self.is_valid_address_value(attr_obj_address))
#txt, truncated = memoryMap.read_cstring(attr_obj_address, max_size)
# 2015-11-05 FIX #20 - read string or wide char string
txt, truncated = attr.read_string(memoryMap, attr_obj_address, max_size)
if truncated:
log.warning('buffer size was too small for this CString: %d', max_size)
# that will SEGFAULT attr.string = txt - instead keepRef to String
self._memory_handler.keepRef(txt, self._ctypes.CString, attr_obj_address)
log.debug('kept CString ref for "%s" at @%x', txt, attr_obj_address)
return True
# not functionType, it's not loadable
elif self._ctypes.is_pointer_type(attrtype):
_attrType = self._utils.get_subtype(attrtype)
attr_obj_address = self._utils.get_pointee_address(attr)
####
# memcpy and save objet ref + pointer in attr
# we know the field is considered valid, so if it's not in
# memory_space, we can ignore it
memoryMap = self.is_valid_address(attr, _attrType)
if not memoryMap:
# big BUG Badaboum, why did pointer changed validity/value ?
log.warning("%s %s not loadable 0x%lx but VALID ", attrname, attr, attr_obj_address)
return True
ref = self._memory_handler.getRef(_attrType, attr_obj_address)
if ref is not None:
log.debug("%s %s loading from references cache %s/0x%lx", attrname, attr, _attrType, attr_obj_address)
# DO NOT CHANGE STUFF SOUPID attr.contents = ref. attr.contents
# will SEGFAULT
return True
log.debug("%s %s loading from 0x%lx (is_valid_address: %s)", attrname, attr, attr_obj_address, memoryMap)
# Read the struct in memory and make a copy to play with.
# DO NOT COPY THE STRUCT, we have a working readStruct for that...
# ERRROR
# attr.contents=_attrType.from_buffer_copy(memoryMap.readStruct(attr_obj_address,
# _attrType ))
contents = memoryMap.read_struct(attr_obj_address, _attrType)
# save that validated and loaded ref and original addr so we dont
# need to recopy it later
self._memory_handler.keepRef(contents, _attrType, attr_obj_address)
log.debug("keepRef %s.%s @%x", _attrType, attrname, attr_obj_address)
log.debug(
"%s %s loaded memcopy from 0x%lx to 0x%lx",
attrname,
attr,
attr_obj_address,
self._utils.get_pointee_address(attr))
# recursive validation checks on new struct
if not bool(attr):
log.warning('Member %s is null after copy: %s', attrname, attr)
return True
# go and load the pointed struct members recursively
subtype = self._utils.get_subtype(attrtype)
if self._ctypes.is_basic_type(subtype) or self._ctypes.is_array_of_basic_type(subtype):
# do nothing
return True
elif self._ctypes.is_array_type(subtype) or self._ctypes.is_pointer_type(subtype):
# FIXME
return self._load_member(record, contents, 'pointee', subtype, record_constraints, max_depth - 1)
log.debug('d: %d load_members recursively on pointer %s' % (max_depth, attrname))
if not self.load_members(contents, max_depth - 1):
log.debug('member %s was not loaded' % attrname)
# invalidate the cache ref.
self._memory_handler.delRef(_attrType, attr_obj_address)
return False
return True
# TATAFN
return True
def is_valid_address(self, obj, structType=None):
"""
:param obj: the obj to evaluate.
:param structType: the object's type, so the size could be taken in consideration.
Returns False if the object address is NULL.
Returns False if the object address is not in a mapping.
Returns the mapping in which the object stands otherwise.
"""
# check for null pointers
addr = self._utils.get_pointee_address(obj)
if addr == 0:
return False
return self.is_valid_address_value(addr, structType)
def is_valid_address_value(self, addr, structType=None):
"""
:param addr: the address to evaluate.
:param structType: the object's type, so the size could be taken in consideration.
Returns False if the object address is NULL.
Returns False if the object address is not in a mapping.
Returns False if the object overflows the mapping.
Returns the mapping in which the address stands otherwise.
"""
m = self._memory_handler.get_mapping_for_address(addr)
log.debug('is_valid_address_value = %x %s' % (addr, m))
if m:
if structType is not None:
s = self._ctypes.sizeof(structType)
if (addr + s) < m.start or (addr + s) > m.end:
return False
return m
return False
def __str__(self):
return "<CTypesRecordConstraintValidator>"
'''
def __str__(self):
"""Print the direct members values. Never tries to recurse."""
target_ctypes = self._memory_handler.get_target_platform().get_target_ctypes()
utils = self._memory_handler.get_ctypes_utils()
if hasattr(self, '_orig_address_'):
s = "# <%s at @%x>\n" % (
self.__class__.__name__, self._orig_address_)
else:
s = "# <%s at @???>\n" % (self.__class__.__name__)
# we need to ensure _mappings_ is defined in all children.
for field, attrtype in self.get_fields():
attr = getattr(self, field)
if self._ctypes.is_basic_type(attrtype):
# basic type, target_ctypes or python
s += '%s : %s, \n' % (field, repr(attr))
elif (self._ctypes.is_struct_type(attrtype) or
self._ctypes.is_union_type(attrtype)):
# you can print a inner struct content
s += '%s (@0x%lx) : {\t%s}\n' % (field,
self._ctypes.addressof(attr),
attr)
elif self._ctypes.is_function_type(attrtype):
# only print address in target space
s += '%s (@0x%lx) : 0x%lx (FIELD NOT LOADED: function type)\n' % (
field, self._ctypes.addressof(attr),
utils.get_pointee_address(attr))
elif self._ctypes.is_array_of_basic_type(attrtype):
try:
s += '%s (@0x%lx) : %s\n' % (field, self._ctypes.addressof(attr),
repr(utils.array2bytes(attr)))
except IndexError as e:
log.error('error while reading %s %s' % (repr(attr),
type(attr)))
# FIXME
elif self._ctypes.is_array_type(attrtype):
# array of something else than int
s += '%s (@0x%lx) :[' % (field, self._ctypes.addressof(attr))
s += ','.join(["%s" % val for val in attr])
s += '],\n'
elif self._ctypes.is_cstring_type(attrtype):
# only print address/null
s += '%s : 0x%lx\n' % (field,
utils.get_pointee_address(attr.ptr))
elif self._ctypes.is_pointer_type(attrtype): # and
# not self._ctypes.is_pointer_to_void_type(attrtype)):
# do not recurse.
if attr is None:
attr = 0
s += '%s : 0x0\n' % field
else:
print attr, type(attr), attrtype
s += '%s : 0x%lx\n' % (field, utils.get_pointee_address(attr))
elif (isinstance(attr, long)) or (isinstance(attr, int)):
s += '%s : %s\n' % (field, hex(attr))
else:
s += '%s : %s\n' % (field, repr(attr))
return s
def __repr__(self):
if hasattr(self, '_orig_address_'):
return "# <%s at @%x>\n" % (self.__class__.__name__,
self._orig_address_)
else:
return "# <%s at @???>\n" % (self.__class__.__name__)
'''