""" import six, builtins import functools, operator, itertools, types import os, logging import math, re, fnmatch import database import ui, internal from internal import utils, interface, exceptions as E import idaapi ## enumerating __matcher__ = utils.matcher() __matcher__.boolean( 'regex', re.search, utils.fcompose( idaapi.get_segm_name if hasattr(idaapi, 'get_segm_name') else idaapi.get_true_segm_name, utils.string.of)) __matcher__.attribute('index', 'index') __matcher__.attribute('identifier', 'name'), __matcher__.attribute('id', 'name') __matcher__.attribute('selector', 'sel') __matcher__.boolean( 'like', lambda v, n: fnmatch.fnmatch(n, v), utils.fcompose( idaapi.get_segm_name if hasattr(idaapi, 'get_segm_name') else idaapi.get_true_segm_name, utils.string.of)) __matcher__.boolean(
class members(object): """ This namespace allows one to interact with the members belonging to an enumeration once the enumeration's id has been determined. This allows one to iterate through all of its members or add and remove values to the enumeration. By default this namespace will yield the names of all of the members of an enumeration. Some examples of using this namespace are:: > eid = enum.by('example_enumeration') > mid = enum.members.add(eid, 'name', 0x1000) > ok = enum.members.remove(eid, mid) > mid = enum.members.by_name(eid, 'name') > mid = enum.members.by_value(eid, 0x1000) > for mid in enum.members.iterate(eid): ... > enum.members.list(e) """ def __new__(cls, enum): '''Yield the name of each member from the enumeration `enum`.''' eid = by(enum) for mid in cls.iterate(eid): yield member.name(mid) return ## scope @classmethod @utils.string.decorate_arguments('name') def add(cls, enum, name, value, **bitmask): """Add an enumeration member `name` with the specified `value` to the enumeration `enum`. If the int, `bitmask`, is specified then used it as the bitmask for the enumeration. """ eid = by(enum) bmask = bitmask.get('bitmask', idaapi.BADADDR & mask(eid)) res = interface.tuplename(name) if isinstance(name, tuple) else name ok = idaapi.add_enum_member(eid, utils.string.to(res), value, bmask) err = { getattr(idaapi, item): item for item in [ 'ENUM_MEMBER_ERROR_NAME', 'ENUM_MEMBER_ERROR_VALUE', 'ENUM_MEMBER_ERROR_ENUM', 'ENUM_MEMBER_ERROR_MASK', 'ENUM_MEMBER_ERROR_ILLV' ] } if ok in err.keys(): raise E.DisassemblerError( u"{:s}.add({:#x}, {!r}, {:#x}{:s}) : Unable to add member to enumeration due to error {:s}({:d})." .format( '.'.join([__name__, cls.__name__]), eid, name, value, u", {:s}".format(utils.string.kwargs(bitmask)) if bitmask else '', err[ok], ok)) return eid new = create = utils.alias(add, 'members') @classmethod def remove(cls, enum, member): '''Remove the specified `member` of the enumeration `enum`.''' eid = by(enum) mid = cls.by(eid, member) return member.remove(mid) delete = destroy = utils.alias(remove, 'members') ## aggregations @classmethod def names(cls, enum): '''Return a set of all the names belonging to the enumeration `enum`.''' eid = by(enum) return {member.name(mid) for mid in cls.iterate(eid)} @classmethod def values(cls, enum): '''Return a set of all the values belonging to the enumeration `enum`.''' eid = by(enum) return {member.value(mid) for mid in cls.iterate(eid)} @classmethod def mapping(cls, enum): '''Return a dictionary mapping all the values values to their names for the enumeration `enum`.''' eid = by(enum) return { member.value(mid): member.name(mid) for mid in cls.iterate(eid) } ## searching @classmethod def by_index(cls, enum, index): '''Return the member identifier for the member of the enumeration `enum` at the specified `index`.''' eid = by(enum) try: return next(mid for i, mid in enumerate(cls.iterate(eid)) if i == index) except StopIteration: pass raise E.MemberNotFoundError( u"{:s}.by_index({:#x}, {:d}) : Unable to locate member by index.". format('.'.join([__name__, cls.__name__]), eid, index)) @classmethod def by_identifier(cls, enum, mid): '''Return the member of the enumeration specified by `enum` and its `mid`.''' eid = by(enum) if member.parent(mid) != eid: raise E.MemberNotFoundError( u"{:s}.by_identifier({:#x}, {:d}) : Unable to locate member by the specified identifier." .format('.'.join([__name__, cls.__name__]), eid, mid)) return mid @classmethod def by_value(cls, enum, value): '''Return the member identifier for the member of the enumeration `enum` with the specified `value`.''' eid = by(enum) bmask = idaapi.BADADDR & mask(eid) res, _ = idaapi.get_first_serial_enum_member(eid, value, bmask) if res == idaapi.BADADDR: raise E.MemberNotFoundError( u"{:s}.by_value({:#x}, {:d}) : Unable to locate member by value." .format('.'.join([__name__, cls.__name__]), eid, value)) return res byvalue = utils.alias(by_value, 'members') @classmethod @utils.string.decorate_arguments('name') def by_name(cls, enum, name): '''Return the member identifier for the member of the enumeration `enum` with the specified `name`.''' eid = by(enum) for mid in cls.iterate(eid): if name == member.name(mid): return mid continue return byname = utils.alias(by_name, 'members') @utils.multicase(n=six.integer_types) @classmethod def by(cls, enum, n): '''Return the member belonging to `enum` identified by its index or id in `n`.''' return cls.by_identifier( enum, n) if interface.node.is_identifier(n) else cls.by_index( enum, n) @utils.multicase(member=six.string_types) @classmethod @utils.string.decorate_arguments('member') def by(cls, enum, member): '''Return the member with the given `name` belonging to `enum`.''' return cls.by_name(enum, member) # FIXME: Implement a matcher class for enumeration members that can be used with .iterate and .list below. __member_matcher = utils.matcher() @classmethod def __iterate__(cls, eid): '''Iterate through all the members of the enumeration identified by `eid`.''' bmask = idaapi.BADADDR & mask(eid) res = idaapi.get_first_enum_member(eid, bmask) if res == idaapi.BADADDR: return yield res while res != idaapi.get_last_enum_member(eid, bmask): res = idaapi.get_next_enum_member(eid, res, bmask) yield res return @classmethod def iterate(cls, enum): '''Iterate through all ids of each member associated with the enumeration `enum`.''' eid = by(enum) bmask = idaapi.BADADDR & mask(eid) for value in cls.__iterate__(eid): res, _ = idaapi.get_first_serial_enum_member(eid, value, bmask) # XXX: what does get_next_serial_enum_member and the rest do? yield res return @classmethod def list(cls, enum): '''List all the members belonging to the enumeration identified by `enum`.''' # FIXME: make this consistent with every other .list using the matcher class eid = by(enum) listable = [item for item in cls.iterate(eid)] maxindex = max( builtins.map(utils.first, enumerate(listable) ) if listable else [1]) maxvalue = max( builtins.map(utils.fcompose(member.value, "{:#x}".format, len ), listable) if listable else [1]) for i, mid in enumerate(listable): six.print_(u"[{:d}] 0x{:>0{:d}x} {:s}".format( i, member.value(mid), maxvalue, member.name(mid))) return
class members_t(object): """An abstraction around the members of a particular IDA structure This allows one to treat each member as a dict. """ __slots__ = ('__owner', 'baseoffset') # members state @property def owner(self): '''Return the structure_t that owns this members_t.''' return self.__owner @property def ptr(self): '''Return the members' idaapi pointer.''' return self.__owner.ptr.members def __init__(self, owner, baseoffset=0): self.__owner = owner self.baseoffset = baseoffset def __getstate__(self): return (self.owner.name, self.baseoffset, map(self.__getitem__, range(len(self)))) def __setstate__(self, state): ownername, baseoffset, _ = state identifier = idaapi.get_struc_id(ownername) if identifier == idaapi.BADADDR: raise LookupError( "{:s}.instance({:s}).members.__setstate__ : Failure creating a members_t for structure_t {!r}" .format(__name__, self.owner.name, ownername)) logging.warn( "{:s}.instance({:s}).members.__setstate__ : Creating structure {:s} -- [{:+#x}] {:d} members" .format(__name__, self.owner.name, ownername, baseoffset, len(members))) identifier = idaapi.add_struc(idaapi.BADADDR, ownername) self.baseoffset = baseoffset self.__owner = instance(identifier, offset=baseoffset) return # fetching members def __len__(self): '''Return the number of members within the structure.''' return 0 if self.owner.ptr is None else self.owner.ptr.memqty def __iter__(self): for idx in xrange(len(self)): yield member_t(self.owner, idx) return def __getitem__(self, index): '''Return the member at the specified ``index``.''' if isinstance(index, (int, long)): index = self.owner.ptr.memqty + index if index < 0 else index res = member_t( self.owner, index ) if index >= 0 and index < self.owner.ptr.memqty else None elif isinstance(index, str): res = self.byname(index) elif isinstance(index, slice): res = [self.__getitem__(i) for i in range(self.owner.ptr.memqty)].__getitem__(index) else: raise TypeError, index if res is None: raise IndexError, index return res def index(self, member_t): '''Return the index of the member ``member_t``.''' for i in range(0, self.owner.ptr.memqty): if member_t.id == self[i].id: return i continue raise ValueError( "{:s}.instance({:s}).members.index : {!r} not in list".format( __name__, self.owner.name, member_t)) __member_matcher = utils.matcher() __member_matcher.boolean('regex', re.search, 'name') __member_matcher.attribute('index', 'index') __member_matcher.attribute('identifier', 'id'), __matcher__.attribute('id', 'id') __member_matcher.attribute('offset', 'offset') __member_matcher.boolean('name', lambda v, n: fnmatch.fnmatch(n, v), 'name') __member_matcher.boolean('like', lambda v, n: fnmatch.fnmatch(n, v), 'name') __member_matcher.boolean('fullname', lambda v, n: fnmatch.fnmatch(n, v), 'fullname') __member_matcher.boolean('comment', lambda v, n: fnmatch.fnmatch(n, v), 'comment') __member_matcher.boolean( 'greater', operator.le, lambda m: m.offset + m.size), __member_matcher.boolean( 'gt', operator.lt, lambda m: m.offset + m.size) __member_matcher.boolean('less', operator.ge, 'offset'), __member_matcher.boolean( 'lt', operator.gt, 'offset') __member_matcher.predicate('predicate'), __member_matcher.predicate('pred') # searching members @utils.multicase() def iterate(self, **type): if not type: type = {'predicate': lambda n: True} res = __builtin__.list(iter(self)) for k, v in type.iteritems(): res = __builtin__.list(self.__member_matcher.match(k, v, res)) for n in res: yield n @utils.multicase(string=basestring) def list(self, string): '''List any members that match the glob in `string`.''' return self.list(like=string) @utils.multicase() def list(self, **type): """List all the members within the structure. Search type can be identified by providing a named argument. like = glob match regex = regular expression index = particular index identifier = particular id number predicate = function predicate """ res = __builtin__.list(self.iterate(**type)) escape = repr maxindex = max( __builtin__.map( utils.compose(operator.attrgetter('index'), "{:d}".format, len), res) or [1]) maxoffset = max( __builtin__.map( utils.compose(operator.attrgetter('offset'), "{:x}".format, len), res) or [1]) maxsize = max( __builtin__.map( utils.compose(operator.attrgetter('size'), "{:x}".format, len), res) or [1]) maxname = max( __builtin__.map( utils.compose(operator.attrgetter('name'), escape, len), res) or [1]) maxtype = max( __builtin__.map( utils.compose(operator.attrgetter('type'), repr, len), res) or [1]) for m in res: print "[{:{:d}d}] {:>{:d}x}:+{:<{:d}x} {:<{:d}s} {:{:d}s} (flag={:x},dt_type={:x}{:s}){:s}".format( m.index, maxindex, m.offset, int(maxoffset), m.size, maxsize, escape(m.name), int(maxname), m.type, int(maxtype), m.flag, m.dt_type, '' if m.typeid is None else ",typeid={:x}".format(m.typeid), " // {:s}".format(m.comment) if m.comment else '') return @utils.multicase() def by(self, **type): '''Return the member with the specified ``name``.''' searchstring = ', '.join("{:s}={!r}".format(k, v) for k, v in type.iteritems()) res = __builtin__.list(self.iterate(**type)) if len(res) > 1: map(logging.info, (("[{:d}] {:x}:+{:x} '{:s}' {!r}".format( m.index, m.offset, m.size, m.name, m.type)) for m in res)) logging.warn( "{:s}.instance({:s}).members.by({:s}) : Found {:d} matching results, returning the first one. : [{:d}] {:x}:+{:x} '{:s}' {!r}" .format(__name__, self.owner.name, searchstring, len(res), res[0].index, res[0].offset, res[0].size, res[0].fullname, res[0].type)) res = next(iter(res), None) if res is None: raise LookupError( "{:s}.instance({:s}).members.by({:s}) : Found 0 matching results." .format(__name__, self.owner.name, searchstring)) return res @utils.multicase(name=basestring) def by(self, name): '''Return the member with the specified ``name``.''' return self.by_name(name) @utils.multicase(offset=six.integer_types) def by(self, offset): '''Return the member at the specified ``offset``.''' return self.by_offset(offset) def by_name(self, name): '''Return the member with the specified ``name``.''' mem = idaapi.get_member_by_name(self.owner.ptr, str(name)) if mem is None: raise KeyError( "{:s}.instance({:s}).members.by_name : Unable to find member with requested name : {!r}" .format(__name__, self.owner.name, name)) index = self.index(mem) return self[index] byname = byName = utils.alias(by_name, 'members_t') def by_fullname(self, fullname): '''Return the member with the specified ``fullname``.''' mem = idaapi.get_member_by_fullname(self.owner.ptr, str(fullname)) if mem is None: raise KeyError( "{:s}.instance({:s}).members.by_fullname : Unable to find member with full name : {!r}" .format(__name__, self.owner.name, fullname)) index = self.index(mem) return self[index] byfullname = byFullname = utils.alias(by_fullname, 'members_t') def by_offset(self, offset): '''Return the member at the specified ``offset``.''' min, max = map(lambda sz: sz + self.baseoffset, (idaapi.get_struc_first_offset(self.owner.ptr), idaapi.get_struc_last_offset(self.owner.ptr))) mptr = idaapi.get_member(self.owner.ptr, max - self.baseoffset) msize = idaapi.get_member_size(mptr) if (offset < min) or (offset >= max + msize): raise LookupError( "{:s}.instance({:s}).members.by_offset : Requested offset {:+#x} not within bounds ({:#x},{:#x})" .format(__name__, self.owner.name, offset, min, max + msize)) mem = idaapi.get_member(self.owner.ptr, offset - self.baseoffset) if mem is None: raise LookupError( "{:s}.instance({:s}).members.by_offset : Unable to find member at offset : {:+#x}" .format(__name__, self.owner.name, offset)) index = self.index(mem) return self[index] byoffset = byOffset = utils.alias(by_offset, 'members_t') def near_offset(self, offset): '''Return the member near to the specified ``offset``.''' min, max = map(lambda sz: sz + self.baseoffset, (idaapi.get_struc_first_offset(self.owner.ptr), idaapi.get_struc_last_offset(self.owner.ptr))) if (offset < min) or (offset >= max): logging.warn( "{:s}.instance({:s}).members.near_offset : Requested offset {:+#x} not within bounds ({:#x},{:#x}). Trying anyways.." .format(__name__, self.owner.name, offset, min, max)) res = offset - self.baseoffset mem = idaapi.get_member(self.owner.ptr, res) if mem is None: logging.info( "{:s}.instance({:s}).members.near_offset : Unable to locate member at offset {:+#x}. Trying get_best_fit_member instead." .format(__name__, self.owner.name, res)) mem = idaapi.get_best_fit_member(self.owner.ptr, res) if mem is None: raise LookupError( "{:s}.instance({:s}).members.near_offset : Unable to find member near offset : {:+#x}" .format(__name__, self.owner.name, offset)) index = self.index(mem) return self[index] near = nearoffset = nearOffset = utils.alias(near_offset, 'members_t') # adding/removing members @utils.multicase(name=(basestring, tuple)) def add(self, name): '''Append the specified member ``name`` with the default type at the end of the structure.''' offset = self.owner.size + self.baseoffset return self.add(name, int, offset) @utils.multicase(name=(basestring, tuple)) def add(self, name, type): '''Append the specified member ``name`` with the given ``type`` at the end of the structure.''' offset = self.owner.size + self.baseoffset return self.add(name, type, offset) @utils.multicase(name=(basestring, tuple), offset=six.integer_types) def add(self, name, type, offset): """Add a member at ``offset`` with the given ``name`` and ``type``. To specify a particular size, ``type`` can be a tuple with the second element referring to the size. """ flag, typeid, nbytes = interface.typemap.resolve(type) # FIXME: handle .strtype (strings), .ec (enums), .cd (custom) opinfo = idaapi.opinfo_t() opinfo.tid = typeid realoffset = offset - self.baseoffset if name is None: logging.warn( "{:s}.instance({:s}).members.add : name is undefined, defaulting to offset {:+#x}" .format(__name__, self.owner.name, realoffset)) name = 'v', realoffset if isinstance(name, tuple): name = interface.tuplename(*name) res = idaapi.add_struc_member(self.owner.ptr, name, realoffset, flag, opinfo, nbytes) if res == idaapi.STRUC_ERROR_MEMBER_OK: logging.info( "{:s}.instance({:s}).members.add : idaapi.add_struc_member(sptr={!r}, fieldname={:s}, offset={:+#x}, flag={:#x}, mt={:#x}, nbytes={:#x}) : Success" .format(__name__, self.owner.name, self.owner.name, name, realoffset, flag, typeid, nbytes)) else: error = { idaapi.STRUC_ERROR_MEMBER_NAME: 'Duplicate field name', idaapi.STRUC_ERROR_MEMBER_OFFSET: 'Invalid offset', idaapi.STRUC_ERROR_MEMBER_SIZE: 'Invalid size', } callee = "idaapi.add_struc_member(sptr={!r}, fieldname={:s}, offset={:+#x}, flag={:#x}, mt={:#x}, nbytes={:#x})".format( self.owner.name, name, realoffset, flag, typeid, nbytes) logging.fatal(' : '.join( ('members_t.add', callee, error.get(res, "Error code {:#x}".format(res))))) return None res = idaapi.get_member(self.owner.ptr, realoffset) if res is None: logging.fatal( "{:s}.instance({:s}.members.add : Failed creating member {!r} {:s}:{:+#x}" .format(__name__, self.owner.name, name, realoffset, nbytes)) # sloppily figure out what the correct index is idx = self.index(idaapi.get_member(self.owner.ptr, realoffset)) return member_t(self.owner, idx) def pop(self, index): '''Remove the member at the specified ``index``.''' item = self[index] return self.remove(item.offset - self.baseoffset) def __delitem__(self, index): return self.pop(index) @utils.multicase() def remove(self, offset): '''Remove all the member from the structure at ``offset``.''' return idaapi.del_struc_member(self.owner.ptr, offset - self.baseoffset) @utils.multicase() def remove(self, offset, size): '''Remove all the members from the structure from ``offset`` up to ``size``.''' ofs = offset - self.baseoffset return idaapi.del_struc_members(self.owner.ptr, ofs, ofs + size) def __repr__(self): '''Display all the fields within the specified structure.''' result = [] mn, ms = 0, 0 for i in xrange(len(self)): m = self[i] name, t, ofs, size, comment = m.name, m.type, m.offset, m.size, m.comment result.append((i, name, t, ofs, size, comment)) mn = max((mn, len(name))) ms = max((ms, len("{:x}".format(size)))) mi = len(str(len(self))) mo = max( map( len, map("{:x}".format, (self.baseoffset, self.baseoffset + self.owner.size)))) return "{!r}\n{:s}".format( self.owner, '\n'.join( "[{:{:d}d}] {:>{:d}x}:+{:<{:d}x} {:<{:d}s} {!r} {:s}".format( i, mi, o, mo, s, ms, "'{:s}'".format(n), mn + 2, t, " // {:s}".format(c) if c else '') for i, n, t, o, s, c in result))
def mask(enum): '''Return the bitmask for the enumeration `enum`.''' eid = by(enum) res = size(eid) return 2**res-1 if res > 0 else idaapi.BADADDR def repr(enum): '''Return a printable summary of the enumeration `enum`.''' eid = by(enum) w = size(eid)*2 res = [(member.name(n), member.value(n), member.mask(n), member.comment(n)) for n in members.iterate(eid)] aligned = max([len(n) for n, _, _, _ in res] or [0]) return "<type 'enum'> {:s}\n".format(name(eid)) + '\n'.join(("[{:d}] {:<{align}s} : {:#0{width}x} & {:#0{width}x}".format(i, name, value, bmask, width=w+2, align=aligned)+((' // '+comment) if comment else '') for i,(name,value,bmask,comment) in enumerate(res))) # XXX __matcher__ = utils.matcher() __matcher__.attribute('index', idaapi.get_enum_idx) __matcher__.boolean('regex', re.search, utils.fcompose(idaapi.get_enum_name, utils.string.of)) __matcher__.boolean('like', lambda v, n: fnmatch.fnmatch(n, v), utils.fcompose(idaapi.get_enum_name, utils.string.of)) __matcher__.boolean('name', operator.eq, utils.fcompose(idaapi.get_enum_name, utils.string.of)) __matcher__.attribute('id') __matcher__.attribute('identifier') __matcher__.predicate('pred') __matcher__.predicate('predicate') def __iterate__(): '''Yield the identifier of each enumeration within the database.''' for n in six.moves.range(idaapi.get_enum_qty()): yield idaapi.getn_enum(n) return
class member(object): '''This class allows one to interact with the members of a defined enumeration. Examples: e = enum.byName('example_enumeration') print enum.repr(e) enum.member.rename(e, 'oldname', 'newname') n = enum.member.add(e, 'name', 0x1000) enum.member.remove(n) n = enum.member.byName(e, 'name') n = enum.member.byValue(e, 0x1000) enum.member.name(n, 'somename') enum.member.value(n, 0x100) enum.member.comment(n, 'This is an test value') for n in enum.member.iterate(e): print enum.member.name(n) print enum.member.value(n) print enum.member.comment(n) ''' @classmethod def parent(cls, mid): '''Return the enumeration id that owns the member ``mid``.''' return idaapi.get_enum_member_enum(mid) ## lifetime @classmethod def add(cls, enum, name, value, **bitmask): """Add an enumeration member ``name`` with the specified ``value`` to the enumeration identified by ``enum``. If the int, ``bitmask``, is specified then used it as the bitmask for the enumeration. """ eid = by(enum) bmask = bitmask.get('bitmask', -1 & mask(eid)) res = interface.tuplename(name) if isinstance(name, tuple) else name ok = idaapi.add_enum_member(eid, res, value, bmask) if ok in (idaapi.ENUM_MEMBER_ERROR_NAME, idaapi.ENUM_MEMBER_ERROR_VALUE, idaapi.ENUM_MEMBER_ERROR_ENUM, idaapi.ENUM_MEMBER_ERROR_MASK, idaapi.ENUM_MEMBER_ERROR_ILLV): raise ValueError( "{:s}.add({:x}, {!r}, {:x}, bitmask={!r}) : Unable to add member to enumeration." .format('.'.join((__name__, cls.__name__)), eid, name, value, bitmask)) return cls.by_value(eid, value) new = create = utils.alias(add, 'member') @utils.multicase(mid=six.integer_types) @classmethod def remove(cls, mid): '''Remove the enumeration member with the given ``mid``.''' value = cls.value(mid) # XXX: is a serial of 0 valid? res = idaapi.del_enum_member(cls.parent(mid), value, 0, -1 & cls.mask(mid)) if not res: raise LookupError( "{:s}.member._remove({:x}) : Unable to remove member from enumeration." .format(__name__, mid)) return res @utils.multicase() @classmethod def remove(cls, enum, member): '''Remove the specified ``member`` of the enumeration ``enum``.''' eid = by(enum) mid = cls.by(eid, member) return cls.remove(mid) delete = destroy = utils.alias(remove, 'member') ## searching @classmethod def by_index(cls, enum, index): '''Return the member id for the member of the enumeration ``enum`` at the specified ``index``.''' eid = by(enum) try: return next(m for i, m in enumerate(cls.iterate(eid)) if i == index) except StopIteration: pass raise LookupError( "{:s}.by_index({:x}, {:d}) : Unable to locate member by index.". format('.'.join((__name__, cls.__name__)), eid, index)) @classmethod def by_identifer(cls, enum, mid): eid = by(enum) if cls.parent(mid) != eid: raise LookupError( "{:s}.by_identifier({:x}, {:d}) : Unable to locate member by id." .format('.'.join((__name__, cls.__name__)), eid, index)) return mid @classmethod def by_value(cls, enum, value): '''Return the member id for the member of the enumeration ``enum`` with the specified ``value``.''' eid = by(enum) bmask = -1 & mask(eid) res, _ = idaapi.get_first_serial_enum_member(eid, value, bmask) if res == idaapi.BADADDR: raise LookupError( "{:s}.by_value({:x}, {:d}) : Unable to locate member by value." .format('.'.join((__name__, cls.__name__)), eid, value)) return res byValue = utils.alias(by_value, 'member') @classmethod def by_name(cls, enum, name): '''Return the member id for the member of the enumeration ``enum`` with the specified ``name``.''' eid = by(enum) for mid in cls.iterate(eid): if name == cls.name(mid): return mid continue return byName = utils.alias(by_name, 'member') @utils.multicase(n=six.integer_types) @classmethod def by(cls, enum, n): '''Return the member belonging to ``enum`` identified by it's index, or it's id.''' bits = int(math.ceil(math.log(idaapi.BADADDR) / math.log(2.0))) highbyte = 0xff << (bits - 8) if n & highbyte == highbyte: return cls.by_identifier(enum, n) return cls.by_index(enum, n) @utils.multicase(member=basestring) @classmethod def by(cls, enum, member): '''Return the member with the given ``name`` belonging to ``enum``.''' return cls.by_name(enum, member) ## properties @utils.multicase(mid=six.integer_types) @classmethod def name(cls, mid): '''Return the name of the enumeration member ``mid``.''' return idaapi.get_enum_member_name(mid) @utils.multicase() @classmethod def name(cls, enum, member): '''Return the name of the enumeration ``member`` belonging to ``enum``.''' eid = by(enum) mid = cls.by(eid, member) return cls.name(mid) @utils.multicase(mid=six.integer_types, name=(basestring, tuple)) @classmethod def name(cls, mid, name): '''Rename the enumeration member ``mid`` to ``name``.''' res = interface.tuplename(*name) if isinstance(name, tuple) else name return idaapi.set_enum_member_name(mid, res) @utils.multicase(name=basestring) @classmethod def name(cls, enum, member, name, *suffix): '''Rename the enumeration ``member`` of ``enum`` to ``name```.''' eid = by(enum) mid = cls.by(eid, member) res = (name, ) + suffix return cls.name(eid, interface.tuplename(*res)) rename = utils.alias(name, 'member') @utils.multicase(mid=six.integer_types) @classmethod def comment(cls, mid, **repeatable): """Return the comment for the enumeration member id ``mid``. If the bool ``repeatable`` is specified, then return the repeatable comment. """ return idaapi.get_enum_member_cmt(mid, repeatable.get('repeatable', True)) @utils.multicase(name=basestring) @classmethod def comment(cls, enum, member, **repeatable): '''Return the comment for the enumeration ``member`` belonging to ``enum``.''' eid = by(enum) mid = cls.by(eid, name) return cls.comment(mid, **repeatable) @utils.multicase(mid=six.integer_types, comment=basestring) @classmethod def comment(cls, mid, comment, **repeatable): """Set the comment for the enumeration member id ``mid`` to ``comment``. If the bool ``repeatable`` is specified, then set the repeatable comment. """ return idaapi.set_enum_member_cmt(mid, comment, kwds.get('repeatable', True)) @utils.multicase(comment=basestring) @classmethod def comment(cls, enum, member, comment, **repeatable): '''Set the comment for the enumeration ``member`` belonging to ``enum`` to the string ``comment``.''' eid = by(enum) mid = cls.by(eid, name) return cls.comment(mid, comment, **repeatable) @utils.multicase(mid=six.integer_types) @classmethod def value(cls, mid): '''Return the value of the enumeration member ``mid``.''' return idaapi.get_enum_member_value(mid) @utils.multicase() @classmethod def value(cls, enum, member): '''Return the value of the specified ``member`` belonging to the enumeration ``enum``.''' eid = by(enum) mid = cls.by(member) return cls.value(mid) @utils.multicase(value=six.integer_types) @classmethod def value(cls, enum, member, value, **bitmask): """Set the ``value`` for the enumeration ``member`` belonging to ``enum``. If the integer ``bitmask`` is specified, then use it as a bitmask. Otherwise assume all bits are set. """ eid = by(enum) mid = cls.by(enum, member) #bmask = bitmask.get('bitmask', -1 & mask(eid)) bmask = bitmask.get('bitmask', -1 & cls.mask(mid)) return idaapi.set_enum_member_value(mid, value, bmask) @utils.multicase(mid=six.integer_types) @classmethod def serial(cls, mid): '''Return the serial of the enumeration member ``mid``.''' return idaapi.get_enum_member_serial(mid) @utils.multicase() @classmethod def serial(cls, enum, member): '''Return the serial of the enumeration ``member`` belonging to ``enum``.''' eid = by(enum) mid = cls.by(eid, member) return cls.serial(mid) @utils.multicase(mid=six.integer_types) @classmethod def mask(cls, mid): '''Return the bitmask for the enumeration member ``mid``.''' return idaapi.get_enum_member_bmask(mid) @utils.multicase() @classmethod def mask(cls, enum, member): '''Return the bitmask for the enumeration ``member`` belonging to ``enum``.''' eid = by(enum) mid = cls.by(eid, member) return cls.mask(mid) # FIXME __member_matcher = utils.matcher() @classmethod def __iterate__(cls, eid): bmask = -1 & mask(eid) res = idaapi.get_first_enum_member(eid, bmask) if res == idaapi.BADADDR: return yield res while res != idaapi.get_last_enum_member(eid, bmask): res = idaapi.get_next_enum_member(eid, res, bmask) yield res return @classmethod def iterate(cls, enum): '''Iterate through all the member ids associated with the enumeration ``enum``.''' eid = by(enum) bmask = -1 & mask(eid) for v in cls.__iterate__(eid): res, _ = idaapi.get_first_serial_enum_member(eid, v, bmask) # XXX: what does get_next_serial_enum_member and the rest do? yield res return @classmethod def list(cls, enum): # FIXME: make this consistent with every other .list eid = by(enum) res = __builtin__.list(cls.iterate(eid)) maxindex = max(__builtin__.map(utils.first, enumerate(res)) or [1]) maxvalue = max( __builtin__.map(utils.compose(cls.value, "{:x}".format, len), res) or [1]) for i, mid in enumerate(res): print("[{:d}] {:>0{:d}x} {:s}".format(i, cls.value(mid), maxvalue, cls.name(mid))) return