Loading...
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 | """ Custom pointer support This module provides support for special pointer types that are not native to the language used by the target being debugged. Such pointers may be represented as a struct or class (for example IOKit's shared pointers). A custom pointer class must subclass the PointerPolicy class and implement all of its abstract methods. The MetaPointerPolicy metaclass ensures that all known subclasses are registered in a global list (wherever they are located in the lldb macro sources). A client can obtain a PointerPolicy instance by calling the match method with an SBValue instance as an argument. The returned value is one of: * None - the match was unsuccessful and this SBValue instance is not a pointer. * Concrete instance - An instance of the concrete PointerPolicy class that will handle pointer operations for the given SBValue. Concrete policy instances implement an API that allows a client to operate on a value like a native pointer (for example unwrapping a native pointer from a smart pointer). Example: # Obtain an SBValue instance. val = kern.global.GlobalVariable.GetSBValue() # Try to match the pointer policy for the given value. policy = PointerPolicy.match(val) # Unwrap the pointer SBValue. if policy: val = policy.GetPointerSBValue(val) ... Operate on val as usual. """ from __future__ import absolute_import, division, print_function from builtins import map, filter from operator import methodcaller from abc import ABCMeta, abstractmethod import six import lldb from .caching import cache_statically class MetaPointerPolicy(ABCMeta): """ Register a custom pointer policy in global list. """ classes = [] def __new__(cls, clsname, bases, args): newcls = super(MetaPointerPolicy, cls).__new__(cls, clsname, bases, args) cls.classes.append(newcls) return newcls class Singleton(MetaPointerPolicy): """ Meta class for creation of singleton instances. """ _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] @six.add_metaclass(ABCMeta) class PointerPolicy(object): """ Abstract base class common to every custom pointer policy. """ @classmethod def match(cls, sbvalue): """ Match pointer representation based on given SBValue. """ matching = filter(bool, map(methodcaller('match', sbvalue), MetaPointerPolicy.classes)) return next(matching, None) @abstractmethod def GetPointerSBValue(self, sbvalue): """ Returns pointer value that debugger should operate on. """ # Pointers need to have their TBI byte stripped if in use. TBI KASan, # for instance, tags pointers to detect improper memory accesses. Reading # values from such tagged pointers fails. # # Stripping the pointers requires to learn whether TBI is in use or not. # We do that by checking presence of 'kasan_tbi_enabled' symbol which only # exists on the TBI KASan variant. Since KASan is one of more TBI # consumers (along with PAC or Sandbox) this is not an ideal approach. # Inspecting respective CPU state would be more appropriate. @six.add_metaclass(Singleton) class NativePointer(PointerPolicy): """ Policy for native pointers. Strips top bits of a pointer if TBI is in use. Otherwise pointer is used as-is. Native pointers do not have any per-pointer attributes so this policy can be singleton instance. """ @staticmethod @cache_statically def isKasanTBI(target=None): """ Returns true on TBI KASan targets, false otherwise. """ return any(target.FindGlobalVariables('kasan_tbi_enabled', 1)) @classmethod def match(cls, sbvalue): return cls() if sbvalue.GetType().IsPointerType() else None def stripTBI(self, sbvalue): """ Strips the TBI byte value. Since the value is not a plain value but represents a value of a variable, a register or an expression the conversion is performed by (re-)creating the value through expression. """ if sbvalue.GetValueAsAddress() != sbvalue.GetValueAsUnsigned(): addr = sbvalue.GetValueAsAddress() sbv_new = sbvalue.CreateValueFromExpression(None, '(void *)' + str(addr)) return sbv_new.Cast(sbvalue.GetType()) return sbvalue def GetPointerSBValue(self, sbvalue): if self.isKasanTBI(): return self.stripTBI(sbvalue) return sbvalue |