You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
psrGROUP/env/Lib/site-packages/haystack/utils.py

255 lines
9.1 KiB

#!/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