1
2""" Please make sure you read the README COMPLETELY BEFORE reading anything below.
3    It is very critical that you read coding guidelines in Section E in README file.
4"""
5from .cvalue import value
6from . import iterators as ccol
7from .caching import (
8    LazyTarget,
9    dyn_cached_property,
10    cache_dynamically,
11    cache_statically,
12)
13from utils import *
14
15import lldb
16
17class UnsupportedArchitectureError(RuntimeError):
18    def __init__(self, arch, msg="Unsupported architecture"):
19        self._arch = arch
20        self._msg = msg
21        super().__init__(msg)
22
23    def __str__(self):
24        return '%s: %s' % (self._arch, self._msg)
25
26
27def IterateTAILQ_HEAD(headval, element_name, list_prefix=''):
28    """ iterate over a TAILQ_HEAD in kernel. refer to bsd/sys/queue.h
29        params:
30            headval      - value : value object representing the head of the list
31            element_name - str   :  string name of the field which holds the list links.
32            list_prefix  - str   : use 's' here to iterate STAILQ_HEAD instead
33        returns:
34            A generator does not return. It is used for iterating.
35            value : an object that is of type as headval->tqh_first. Always a pointer object
36        example usage:
37          list_head = kern.GetGlobalVariable('mountlist')
38          for entryobj in IterateTAILQ_HEAD(list_head, 'mnt_list'):
39            print GetEntrySummary(entryobj)
40    """
41
42    next_path = ".{}.{}tqe_next".format(element_name, list_prefix)
43    head = headval.GetSBValue()
44
45    return (value(e.AddressOf()) for e in ccol.iter_linked_list(
46        head.Dereference() if head.TypeIsPointerType() else head,
47        next_path,
48        list_prefix + 'tqh_first',
49    ))
50
51
52def IterateLinkedList(headval, field_name):
53    """ iterate over a linked list.
54        This is equivalent to elt = headval; while(elt) { do_work(elt); elt = elt-><field_name>; }
55        params:
56            headval - value : value object representing element in the list.
57            field_name - str       : name of field that holds pointer to next element
58        returns: Nothing. This is used as iterable
59        example usage:
60            first_zone = kern.GetGlobalVariable('first_zone')
61            for zone in IterateLinkedList(first_zone, 'next_zone'):
62                print GetZoneSummary(zone)
63    """
64
65    head = headval.GetSBValue()
66
67    return (value(e.AddressOf()) for e in ccol.iter_linked_list(head, field_name))
68
69
70def IterateListEntry(headval, field_name, list_prefix=''):
71    """ iterate over a list as defined with LIST_HEAD in bsd/sys/queue.h
72        params:
73            headval      - value : Value object for lh_first
74            field_name   - str   : Name of the field in next element's structure
75            list_prefix  - str   : use 's' here to iterate SLIST_HEAD instead
76        returns:
77            A generator does not return. It is used for iterating
78            value  : an object thats of type (element_type) head->le_next. Always a pointer object
79        example usage:
80            headp = kern.globals.initproc.p_children
81            for pp in IterateListEntry(headp, 'p_sibling'):
82                print GetProcInfo(pp)
83    """
84
85    next_path = ".{}.{}le_next".format(field_name, list_prefix)
86    head = headval.GetSBValue()
87
88    return (value(e.AddressOf()) for e in ccol.iter_linked_list(
89        head.Dereference() if head.TypeIsPointerType() else head,
90        next_path,
91        list_prefix + 'lh_first',
92    ))
93
94
95def IterateLinkageChain(queue_head, element_type, field_name):
96    """ Iterate over a Linkage Chain queue in kernel of type queue_head_t. (osfmk/kern/queue.h method 1)
97        This is equivalent to the qe_foreach_element() macro
98        params:
99            queue_head   - value       : Value object for queue_head.
100            element_type - lldb.SBType : pointer type of the element which contains the queue_chain_t. Typically its structs like thread, task etc..
101                         - str         : OR a string describing the type. ex. 'task *'
102            field_name   - str         : Name of the field (in element) which holds a queue_chain_t
103        returns:
104            A generator does not return. It is used for iterating.
105            value  : An object thats of type (element_type). Always a pointer object
106        example usage:
107            coalq = kern.GetGlobalVariable('coalitions_q')
108            for coal in IterateLinkageChain(coalq, 'struct coalition *', 'coalitions'):
109                print GetCoalitionInfo(coal)
110    """
111
112    if isinstance(element_type, str):
113        element_type = gettype(element_type)
114
115    head = queue_head.GetSBValue()
116
117    return (value(e.AddressOf()) for e in ccol.iter_queue_entries(
118        head.Dereference() if head.TypeIsPointerType() else head,
119        element_type.GetPointeeType(),
120        field_name,
121    ))
122
123
124def IterateCircleQueue(queue_head, element_type, field_name):
125    """ iterate over a circle queue in kernel of type circle_queue_head_t. refer to osfmk/kern/circle_queue.h
126        params:
127            queue_head    - lldb.SBValue : Value object for queue_head.
128            element_type  - lldb.SBType : a type of the element 'next' points to. Typically its structs like thread, task etc..
129            field_name    - str : name of the field in target struct.
130        returns:
131            A generator does not return. It is used for iterating.
132            SBValue  : an object thats of type (element_type) queue_head->next. Always a pointer object
133    """
134
135    if isinstance(element_type, str):
136        element_type = gettype(element_type)
137
138    head = queue_head.GetSBValue()
139
140    return (value(e.AddressOf()) for e in ccol.iter_circle_queue(
141        head.Dereference() if head.TypeIsPointerType() else head,
142        element_type,
143        field_name,
144    ))
145
146
147def IterateQueue(queue_head, element_ptr_type, element_field_name, backwards=False, unpack_ptr_fn=None):
148    """ Iterate over an Element Chain queue in kernel of type queue_head_t. (osfmk/kern/queue.h method 2)
149        params:
150            queue_head         - value : Value object for queue_head.
151            element_ptr_type   - lldb.SBType : a pointer type of the element 'next' points to. Typically its structs like thread, task etc..
152                               - str         : OR a string describing the type. ex. 'task *'
153            element_field_name - str : name of the field in target struct.
154            backwards          - backwards : traverse the queue backwards
155            unpack_ptr_fn      - function : a function ptr of signature def unpack_ptr(long v) which returns long.
156        returns:
157            A generator does not return. It is used for iterating.
158            value  : an object thats of type (element_type) queue_head->next. Always a pointer object
159        example usage:
160            for page_meta in IterateQueue(kern.globals.first_zone.pages.all_free, 'struct zone_page_metadata *', 'pages'):
161                print page_meta
162    """
163
164    if isinstance(element_ptr_type, str):
165        element_ptr_type = gettype(element_ptr_type)
166
167    head = queue_head.GetSBValue()
168
169    return (value(e.AddressOf()) for e in ccol.iter_queue(
170        head.Dereference() if head.TypeIsPointerType() else head,
171        element_ptr_type.GetPointeeType(),
172        element_field_name,
173        backwards=backwards,
174        unpack=unpack_ptr_fn,
175    ))
176
177
178def IterateRBTreeEntry(rootelt, field_name):
179    """ iterate over a rbtree as defined with RB_HEAD in libkern/tree.h
180            rootelt      - value : Value object for rbh_root
181            field_name   - str   : Name of the field in link element's structure
182        returns:
183            A generator does not return. It is used for iterating
184            value  : an object thats of type (element_type) head->sle_next. Always a pointer object
185    """
186
187    return (value(e.AddressOf()) for e in ccol.iter_RB_HEAD(rootelt.GetSBValue(), field_name))
188
189
190def IterateSchedPriorityQueue(root, element_type, field_name):
191    """ iterate over a priority queue as defined with struct priority_queue from osfmk/kern/priority_queue.h
192            root         - value : Value object for the priority queue
193            element_type - str   : Type of the link element
194            field_name   - str   : Name of the field in link element's structure
195        returns:
196            A generator does not return. It is used for iterating
197            value  : an object thats of type (element_type). Always a pointer object
198    """
199
200    if isinstance(element_type, str):
201        element_type = gettype(element_type)
202
203    root = root.GetSBValue()
204
205    return (value(e.AddressOf()) for e in ccol.iter_priority_queue(
206        root.Dereference() if root.TypeIsPointerType() else root,
207        element_type,
208        field_name,
209    ))
210
211
212def IterateMPSCQueue(root, element_type, field_name):
213    """ iterate over an MPSC queue as defined with struct mpsc_queue_head from osfmk/kern/mpsc_queue.h
214            root         - value : Value object for the mpsc queue
215            element_type - str   : Type of the link element
216            field_name   - str   : Name of the field in link element's structure
217        returns:
218            A generator does not return. It is used for iterating
219            value  : an object thats of type (element_type). Always a pointer object
220    """
221    if isinstance(element_type, str):
222        element_type = gettype(element_type)
223
224    return (value(e.AddressOf()) for e in ccol.iter_mpsc_queue(
225        root.GetSBValue(), element_type, field_name
226    ))
227
228
229class KernelTarget(object):
230    """ A common kernel object that provides access to kernel objects and information.
231        The class holds global lists for  task, terminated_tasks, procs, zones, zombroc etc.
232        It also provides a way to symbolicate an address or create a value from an address.
233    """
234    def __init__(self, debugger):
235        """ Initialize the kernel debugging environment.
236            Target properties like architecture and connectedness are lazy-evaluted.
237        """
238
239        self.symbolicator = None
240
241        class _GlobalVariableFind(object):
242            def __init__(self, kern):
243                self._xnu_kernobj_12obscure12 = kern
244            def __getattr__(self, name):
245                v = self._xnu_kernobj_12obscure12.GetGlobalVariable(name)
246                if not v.GetSBValue().IsValid():
247                    # Python 2 swallows all exceptions in hasattr(). That makes it work
248                    # even when global variable is not found. Python 3 has fixed the behavior
249                    # and we can raise only AttributeError here to keep original behavior.
250                    raise AttributeError('No such global variable by name: %s '%str(name))
251                return v
252        self.globals = _GlobalVariableFind(self)
253
254    def _GetSymbolicator(self):
255        """ Internal function: To initialize the symbolication from lldb.utils
256        """
257        if not self.symbolicator is None:
258            return self.symbolicator
259
260        from lldb.utils import symbolication
261        symbolicator = symbolication.Symbolicator()
262        symbolicator.target = LazyTarget.GetTarget()
263        self.symbolicator = symbolicator
264        return self.symbolicator
265
266    def Symbolicate(self, addr):
267        """ simple method to get name of function/variable from an address. this is equivalent of gdb 'output /a 0xaddress'
268            params:
269                addr - int : typically hex value like 0xffffff80002c0df0
270            returns:
271                str - '' if no symbol found else the symbol name.
272            Note: this function only finds the first symbol. If you expect multiple symbol conflict please use SymbolicateFromAddress()
273        """
274        ret_str = ''
275        syms = self.SymbolicateFromAddress(addr)
276        if len(syms) > 0:
277            ret_str +=syms[0].GetName()
278        return ret_str
279
280    def SymbolicateFromAddress(self, addr, fullSymbol=False):
281        """ symbolicates any given address based on modules loaded in the target.
282            params:
283                addr - int : typically hex value like 0xffffff80002c0df0
284            returns:
285                [] of SBSymbol: In case we don't find anything than empty array is returned.
286                      Note: a type of symbol can be figured out by gettype() function of SBSymbol.
287            example usage:
288                syms = kern.Symbolicate(0xffffff80002c0df0)
289                for s in syms:
290                  if s.GetType() == lldb.eSymbolTypeCode:
291                    print "Function", s.GetName()
292                  if s.GetType() == lldb.eSymbolTypeData:
293                    print "Variable", s.GetName()
294        """
295        if type(int(1)) != type(addr):
296            if str(addr).strip().find("0x") == 0 :
297                addr = int(addr, 16)
298            else:
299                addr = int(addr)
300        addr = self.StripKernelPAC(addr)
301        ret_array = []
302        symbolicator = self._GetSymbolicator()
303        syms = symbolicator.symbolicate(addr)
304        if not syms:
305            return ret_array
306        for s in syms:
307            if fullSymbol:
308                ret_array.append(s)
309            else:
310                ret_array.append(s.get_symbol_context().symbol)
311        return ret_array
312
313    def IsDebuggerConnected(self):
314        proc_state = LazyTarget.GetProcess().state
315        if proc_state == lldb.eStateInvalid : return False
316        if proc_state in [lldb.eStateStopped, lldb.eStateSuspended] : return True
317
318    @staticmethod
319    @cache_statically
320    def GetGlobalVariable(name, target=None):
321        """ Get the value object representation for a kernel global variable
322            params:
323              name : str - name of the variable. ex. version
324            returns: value - python object representing global variable.
325            raises : Exception in case the variable is not found.
326        """
327
328        return value(target.FindGlobalVariables(name, 1).GetValueAtIndex(0))
329
330    def PERCPU_BASE(self, cpu):
331        """ Get the PERCPU base for the given cpu number
332            params:
333              cpu  : int - the cpu# for this variable
334            returns: int - the base for PERCPU for this cpu index
335        """
336        if self.arch == 'x86_64':
337            return unsigned(self.globals.cpu_data_ptr[cpu].cpu_pcpu_base)
338        elif self.arch.startswith('arm'):
339            data_entries = self.GetGlobalVariable('CpuDataEntries')
340            BootCpuData = addressof(self.GetGlobalVariable('percpu_slot_cpu_data'))
341            return unsigned(data_entries[cpu].cpu_data_vaddr) - unsigned(BootCpuData)
342
343    def PERCPU_GET(self, name, cpu):
344        """ Get the value object representation for a kernel percpu global variable
345            params:
346              name : str - name of the variable. ex. version
347              cpu  : int - the cpu# for this variable
348            returns: value - python object representing global variable.
349            raises : Exception in case the variable is not found.
350        """
351        var = addressof(self.GetGlobalVariable('percpu_slot_' + name))
352        var_type = var.GetSBValue().GetType().name
353        addr = unsigned(var) + self.PERCPU_BASE(cpu)
354        return dereference(self.GetValueFromAddress(addr, var_type))
355
356    def GetLoadAddressForSymbol(self, name):
357        """ Get the load address of a symbol in the kernel.
358            params:
359              name : str - name of the symbol to lookup
360            returns: int - the load address as an integer. Use GetValueFromAddress to cast to a value.
361            raises : LookupError - if the symbol is not found.
362        """
363        name = str(name)
364        target = LazyTarget.GetTarget()
365        syms_arr = target.FindSymbols(name)
366        if syms_arr.IsValid() and len(syms_arr) > 0:
367            symbol = syms_arr[0].GetSymbol()
368            if symbol.IsValid():
369                return int(symbol.GetStartAddress().GetLoadAddress(target))
370
371        raise LookupError("Symbol not found: " + name)
372
373    def GetValueFromAddress(self, addr: int, type_str: str = 'void *') -> value:
374        """ convert an address to a value
375            params:
376                addr - int : typically hex value like 0xffffff80008dc390
377                type_str - str: type to cast to. Default type will be void *
378            returns:
379                value : a value object which has address as addr and type is type_str
380        """
381        sbv = self.globals.version.GetSBValue().CreateValueFromExpression(None,f"({type_str}){str(addr)}")
382
383        wanted_type = gettype(type_str)
384        if sbv.GetType() != wanted_type:
385            sbv = sbv.Cast(wanted_type)
386
387        return value(sbv)
388
389    def CreateValueFromAddress(self, addr: int, type_str: str = 'void *') -> value:
390        """ convert an address to a value, using `GetValueFromAddress()`
391            params:
392                addr - int : typically hex value like 0xffffff80008dc390
393                type_str - str: type to cast to. Default type will be void *
394            returns:
395                value : a value object which has address as addr and type is type_str
396
397            There are 2 LLDB APIs to create SBValues for data in memory - `CreateValueFromExpression()` and `CreateValueFromAddress()`.
398            The former will parse an expression (like those used in an LLDB print command - `p/x *(vm_map_t)0xFOO_ADDR`).
399            The latter allows telling LLDB "Give me an SBValue that interprets the data begginning at FOO address as BAR type".
400
401            `CreateValueFromAddress()` is more performant, but can be clunkier to work with.
402            However, for simple use cases it can be just as convenient as `CreateValueFromExpression()`.
403            Just take heed that you probably don't want "an SBValue for a pointer to BAR type who's data is at address FOO",
404            rather "an SBValue for BAR type who's data is at address FOO".
405
406            Where performance matters or there's no usability tradeoff, you're encouraged to use `CreateValueFromAddress()` over `GetValueFromAddress()`.
407            The poor, confusing naming is legacy :/
408
409        """
410        sbv = self.globals.version.GetSBValue().xCreateValueFromAddress(None, addr, gettype(type_str))
411        return value(sbv)
412
413    def CreateTypedPointerFromAddress(self, addr, type_str = "char"):
414        """ convert a address to pointer value
415
416            Note: This is obsolete and here as a temporary solution
417                  for people to migrate to using references instead.
418
419            params:
420                addr - int : typically hex value like 0xffffff80008dc390
421                type_str - str: type to cast to, must not be a pointer type.
422            returns:
423                value : a value object which has address as addr
424                        and type is `type_str *`
425        """
426
427        target = LazyTarget.GetTarget()
428        sbv    = target.xCreateValueFromAddress(None, addr, gettype(type_str))
429        return value(sbv.AddressOf())
430
431
432    def GetValueAsType(self, v, t):
433        """ Retrieves a global variable 'v' of type 't' wrapped in a vue object.
434            If 'v' is an address, creates a vue object of the appropriate type.
435            If 'v' is a name, looks for the global variable and asserts its type.
436            Throws:
437                NameError - If 'v' cannot be found
438                TypeError - If 'v' is of the wrong type
439        """
440        if islong(v):
441            return self.GetValueFromAddress(v, t)
442        else:
443            var = LazyTarget.GetTarget().FindGlobalVariables(v, 1)[0]
444            if not var:
445                raise NameError("Failed to find global variable '{0}'".format(v))
446            if var.GetTypeName() != t:
447                raise TypeError("{0} must be of type '{1}', not '{2}'".format(v, t, var.GetTypeName()))
448            return value(var)
449
450    def _GetIterator(self, iter_head_name, next_element_name='next', iter_head_type=None):
451        """ returns an iterator for a collection in kernel memory.
452            params:
453                iter_head_name - str : name of queue_head or list head variable.
454                next_element_name - str : name of the element that leads to next element.
455                                          for ex. in struct zone list 'next_zone' is the linking element.
456            returns:
457                iterable : typically used in conjunction with "for varname in iterable:"
458        """
459        head_element = self.GetGlobalVariable(iter_head_name)
460        return head_element.GetSBValue().linked_list_iter(next_element_name)
461
462    def TruncPage(self, addr):
463        return (addr & ~(unsigned(self.GetGlobalVariable("page_size")) - 1))
464
465    def RoundPage(self, addr):
466        return trunc_page(addr + unsigned(self.GetGlobalVariable("page_size")) - 1)
467
468    def StraddlesPage(self, addr, size):
469        if size > unsigned(self.GetGlobalVariable("page_size")):
470            return True
471        val = ((addr + size) & (unsigned(self.GetGlobalVariable("page_size"))-1))
472        return (val < size and val > 0)
473
474    def StripUserPAC(self, addr):
475        if self.arch != 'arm64e':
476            return addr
477        T0Sz = self.GetGlobalVariable('gT0Sz')
478        return StripPAC(addr, T0Sz)
479
480    def StripKernelPAC(self, addr):
481        if self.arch != 'arm64e':
482            return addr
483        T1Sz = self.GetGlobalVariable('gT1Sz')
484        return StripPAC(addr, T1Sz)
485
486    PAGE_PROTECTION_TYPE_NONE = 0
487    PAGE_PROTECTION_TYPE_PPL = 1
488    PAGE_PROTECTION_TYPE_SPTM = 2
489
490    def PhysToKVARM64(self, addr):
491        if self.globals.page_protection_type <= self.PAGE_PROTECTION_TYPE_PPL:
492            ptov_table = self.globals.ptov_table
493            for i in range(0, self.globals.ptov_index):
494                if (addr >= int(unsigned(ptov_table[i].pa))) and (addr < (int(unsigned(ptov_table[i].pa)) + int(unsigned(ptov_table[i].len)))):
495                    return (addr - int(unsigned(ptov_table[i].pa)) + int(unsigned(ptov_table[i].va)))
496        else:
497            papt_table = self.globals.libsptm_papt_ranges
498            page_size = self.globals.page_size
499            for i in range(0, self.globals.libsptm_n_papt_ranges):
500                if (addr >= int(unsigned(papt_table[i].paddr_start))) and (addr < (int(unsigned(papt_table[i].paddr_start)) + int(unsigned(papt_table[i].num_mappings) * page_size))):
501                    return (addr - int(unsigned(papt_table[i].paddr_start)) + int(unsigned(papt_table[i].papt_start)))
502            raise ValueError("PA {:#x} not found in physical region lookup table".format(addr))
503        return (addr - unsigned(self.globals.gPhysBase) + unsigned(self.globals.gVirtBase))
504
505    def PhysToKernelVirt(self, addr):
506        if self.arch == 'x86_64':
507            return (addr + unsigned(self.GetGlobalVariable('physmap_base')))
508        elif self.arch.startswith('arm64'):
509            return self.PhysToKVARM64(addr)
510        elif self.arch.startswith('arm'):
511            return (addr - unsigned(self.GetGlobalVariable("gPhysBase")) + unsigned(self.GetGlobalVariable("gVirtBase")))
512        else:
513            raise ValueError("PhysToVirt does not support {0}".format(self.arch))
514
515    @cache_statically
516    def GetUsecDivisor(self, target=None):
517        if self.arch == 'x86_64':
518            return 1000
519
520        rtclockdata_addr = self.GetLoadAddressForSymbol('RTClockData')
521        rtc = self.GetValueFromAddress(rtclockdata_addr, 'struct _rtclock_data_ *')
522        return unsigned(rtc.rtc_usec_divisor)
523
524    def GetNanotimeFromAbstime(self, abstime):
525        """ convert absolute time (which is in MATUs) to nano seconds.
526            Since based on architecture the conversion may differ.
527            params:
528                abstime - int absolute time as shown by mach_absolute_time
529            returns:
530                int - nanosecs of time
531        """
532        return (abstime * 1000) // self.GetUsecDivisor()
533
534    @property
535    @cache_statically
536    def zones(self, target=None):
537        za = target.chkFindFirstGlobalVariable('zone_array')
538        zs = target.chkFindFirstGlobalVariable('zone_security_array')
539        n  = target.chkFindFirstGlobalVariable('num_zones').xGetValueAsInteger()
540
541        iter_za = za.chkGetChildAtIndex(0).xIterSiblings(0, n)
542        iter_zs = zs.chkGetChildAtIndex(0).xIterSiblings(0, n)
543
544        return [
545            (value(next(iter_za).AddressOf()), value(next(iter_zs).AddressOf()))
546            for i in range(n)
547        ]
548
549    @property
550    def threads(self):
551        target = LazyTarget.GetTarget()
552
553        return (value(t.AddressOf()) for t in ccol.iter_queue(
554            target.chkFindFirstGlobalVariable('threads'),
555            gettype('thread'),
556            'threads',
557        ))
558
559    @dyn_cached_property
560    def tasks(self, target=None):
561        return [value(t.AddressOf()) for t in ccol.iter_queue(
562            target.chkFindFirstGlobalVariable('tasks'),
563            gettype('task'),
564            'tasks',
565        )]
566
567    @property
568    def coalitions(self):
569        target = LazyTarget.GetTarget()
570
571        return (value(coal.AddressOf()) for coal in ccol.SMRHash(
572            target.chkFindFirstGlobalVariable('coalition_hash'),
573            target.chkFindFirstGlobalVariable('coal_hash_traits'),
574        ))
575
576    @property
577    def thread_groups(self):
578        target = LazyTarget.GetTarget()
579
580        return (value(tg.AddressOf()) for tg in ccol.iter_queue_entries(
581            target.chkFindFirstGlobalVariable('tg_queue'),
582            gettype('thread_group'),
583            'tg_queue_chain',
584        ))
585
586    @property
587    def terminated_tasks(self):
588        target = LazyTarget.GetTarget()
589
590        return (value(t.AddressOf()) for t in ccol.iter_queue(
591            target.chkFindFirstGlobalVariable('terminated_tasks'),
592            gettype('task'),
593            'tasks',
594        ))
595
596    @property
597    def terminated_threads(self):
598        target = LazyTarget.GetTarget()
599
600        return (value(t.AddressOf()) for t in ccol.iter_queue(
601            target.chkFindFirstGlobalVariable('terminated_threads'),
602            gettype('thread'),
603            'threads',
604        ))
605
606    @property
607    def procs(self):
608        target = LazyTarget.GetTarget()
609
610        return (value(p.AddressOf()) for p in ccol.iter_LIST_HEAD(
611            target.chkFindFirstGlobalVariable('allproc'),
612            'p_list',
613        ))
614
615    @property
616    def interrupt_stats(self):
617        target = LazyTarget.GetTarget()
618
619        return (value(stat.AddressOf()) for stat in ccol.iter_queue(
620            target.chkFindFirstGlobalVariable('gInterruptAccountingDataList'),
621            gettype('IOInterruptAccountingData'),
622            'chain',
623        ))
624
625    @property
626    def zombprocs(self):
627        target = LazyTarget.GetTarget()
628
629        return (value(p.AddressOf()) for p in ccol.iter_LIST_HEAD(
630            target.chkFindFirstGlobalVariable('zombproc'),
631            'p_list',
632        ))
633
634    @property
635    def version(self):
636        return str(self.globals.version)
637
638    @property
639    def arch(self):
640        return LazyTarget.GetTarget().triple.split('-', 1)[0]
641
642    @property
643    def ptrsize(self):
644        return LazyTarget.GetTarget().GetAddressByteSize()
645
646    @property
647    def VM_MIN_KERNEL_ADDRESS(self):
648        if self.arch == 'x86_64':
649            return 0xffffff8000000000
650        else:
651            return 0xffffffe00000000
652
653    @property
654    def VM_MIN_KERNEL_AND_KEXT_ADDRESS(self):
655        if self.arch == 'x86_64':
656            return 0xffffff8000000000 - 0x80000000
657        else:
658            return 0xffffffe00000000
659