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 addr = unsigned(var) + self.PERCPU_BASE(cpu) 353 return dereference(self.GetValueFromAddress(addr, var)) 354 355 def GetLoadAddressForSymbol(self, name): 356 """ Get the load address of a symbol in the kernel. 357 params: 358 name : str - name of the symbol to lookup 359 returns: int - the load address as an integer. Use GetValueFromAddress to cast to a value. 360 raises : LookupError - if the symbol is not found. 361 """ 362 name = str(name) 363 target = LazyTarget.GetTarget() 364 syms_arr = target.FindSymbols(name) 365 if syms_arr.IsValid() and len(syms_arr) > 0: 366 symbol = syms_arr[0].GetSymbol() 367 if symbol.IsValid(): 368 return int(symbol.GetStartAddress().GetLoadAddress(target)) 369 370 raise LookupError("Symbol not found: " + name) 371 372 def GetValueFromAddress(self, addr: int, type_str: str = 'void *') -> value: 373 """ convert an address to a value 374 params: 375 addr - int : typically hex value like 0xffffff80008dc390 376 type_str - str: type to cast to. Default type will be void * 377 returns: 378 value : a value object which has address as addr and type is type_str 379 """ 380 sbv = self.globals.version.GetSBValue().CreateValueFromExpression(None,f"({type_str}){str(addr)}") 381 382 wanted_type = gettype(type_str) 383 if sbv.GetType() != wanted_type: 384 sbv = sbv.Cast(wanted_type) 385 386 return value(sbv) 387 388 def CreateValueFromAddress(self, addr: int, type_str: str = 'void *') -> value: 389 """ convert an address to a value, using `GetValueFromAddress()` 390 params: 391 addr - int : typically hex value like 0xffffff80008dc390 392 type_str - str: type to cast to. Default type will be void * 393 returns: 394 value : a value object which has address as addr and type is type_str 395 396 There are 2 LLDB APIs to create SBValues for data in memory - `CreateValueFromExpression()` and `CreateValueFromAddress()`. 397 The former will parse an expression (like those used in an LLDB print command - `p/x *(vm_map_t)0xFOO_ADDR`). 398 The latter allows telling LLDB "Give me an SBValue that interprets the data begginning at FOO address as BAR type". 399 400 `CreateValueFromAddress()` is more performant, but can be clunkier to work with. 401 However, for simple use cases it can be just as convenient as `CreateValueFromExpression()`. 402 Just take heed that you probably don't want "an SBValue for a pointer to BAR type who's data is at address FOO", 403 rather "an SBValue for BAR type who's data is at address FOO". 404 405 Where performance matters or there's no usability tradeoff, you're encouraged to use `CreateValueFromAddress()` over `GetValueFromAddress()`. 406 The poor, confusing naming is legacy :/ 407 408 """ 409 sbv = self.globals.version.GetSBValue().xCreateValueFromAddress(None, addr, gettype(type_str)) 410 return value(sbv) 411 412 def CreateTypedPointerFromAddress(self, addr, type_str = "char"): 413 """ convert a address to pointer value 414 415 Note: This is obsolete and here as a temporary solution 416 for people to migrate to using references instead. 417 418 params: 419 addr - int : typically hex value like 0xffffff80008dc390 420 type_str - str: type to cast to, must not be a pointer type. 421 returns: 422 value : a value object which has address as addr 423 and type is `type_str *` 424 """ 425 426 target = LazyTarget.GetTarget() 427 sbv = target.xCreateValueFromAddress(None, addr, gettype(type_str)) 428 return value(sbv.AddressOf()) 429 430 431 def GetValueAsType(self, v, t): 432 """ Retrieves a global variable 'v' of type 't' wrapped in a vue object. 433 If 'v' is an address, creates a vue object of the appropriate type. 434 If 'v' is a name, looks for the global variable and asserts its type. 435 Throws: 436 NameError - If 'v' cannot be found 437 TypeError - If 'v' is of the wrong type 438 """ 439 if islong(v): 440 return self.GetValueFromAddress(v, t) 441 else: 442 var = LazyTarget.GetTarget().FindGlobalVariables(v, 1)[0] 443 if not var: 444 raise NameError("Failed to find global variable '{0}'".format(v)) 445 if var.GetTypeName() != t: 446 raise TypeError("{0} must be of type '{1}', not '{2}'".format(v, t, var.GetTypeName())) 447 return value(var) 448 449 def _GetIterator(self, iter_head_name, next_element_name='next', iter_head_type=None): 450 """ returns an iterator for a collection in kernel memory. 451 params: 452 iter_head_name - str : name of queue_head or list head variable. 453 next_element_name - str : name of the element that leads to next element. 454 for ex. in struct zone list 'next_zone' is the linking element. 455 returns: 456 iterable : typically used in conjunction with "for varname in iterable:" 457 """ 458 head_element = self.GetGlobalVariable(iter_head_name) 459 return head_element.GetSBValue().linked_list_iter(next_element_name) 460 461 def TruncPage(self, addr): 462 return (addr & ~(unsigned(self.GetGlobalVariable("page_size")) - 1)) 463 464 def RoundPage(self, addr): 465 return trunc_page(addr + unsigned(self.GetGlobalVariable("page_size")) - 1) 466 467 def StraddlesPage(self, addr, size): 468 if size > unsigned(self.GetGlobalVariable("page_size")): 469 return True 470 val = ((addr + size) & (unsigned(self.GetGlobalVariable("page_size"))-1)) 471 return (val < size and val > 0) 472 473 def StripUserPAC(self, addr): 474 if self.arch != 'arm64e': 475 return addr 476 T0Sz = self.GetGlobalVariable('gT0Sz') 477 return StripPAC(addr, T0Sz) 478 479 def StripKernelPAC(self, addr): 480 if self.arch != 'arm64e': 481 return addr 482 T1Sz = self.GetGlobalVariable('gT1Sz') 483 return StripPAC(addr, T1Sz) 484 485 PAGE_PROTECTION_TYPE_NONE = 0 486 PAGE_PROTECTION_TYPE_PPL = 1 487 PAGE_PROTECTION_TYPE_SPTM = 2 488 489 def PhysToKVARM64(self, addr): 490 if self.globals.page_protection_type <= self.PAGE_PROTECTION_TYPE_PPL: 491 ptov_table = self.globals.ptov_table 492 for i in range(0, self.globals.ptov_index): 493 if (addr >= int(unsigned(ptov_table[i].pa))) and (addr < (int(unsigned(ptov_table[i].pa)) + int(unsigned(ptov_table[i].len)))): 494 return (addr - int(unsigned(ptov_table[i].pa)) + int(unsigned(ptov_table[i].va))) 495 else: 496 papt_table = self.globals.libsptm_papt_ranges 497 page_size = self.globals.page_size 498 for i in range(0, self.globals.libsptm_n_papt_ranges): 499 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))): 500 return (addr - int(unsigned(papt_table[i].paddr_start)) + int(unsigned(papt_table[i].papt_start))) 501 raise ValueError("PA {:#x} not found in physical region lookup table".format(addr)) 502 return (addr - unsigned(self.globals.gPhysBase) + unsigned(self.globals.gVirtBase)) 503 504 def PhysToKernelVirt(self, addr): 505 if self.arch == 'x86_64': 506 return (addr + unsigned(self.GetGlobalVariable('physmap_base'))) 507 elif self.arch.startswith('arm64'): 508 return self.PhysToKVARM64(addr) 509 elif self.arch.startswith('arm'): 510 return (addr - unsigned(self.GetGlobalVariable("gPhysBase")) + unsigned(self.GetGlobalVariable("gVirtBase"))) 511 else: 512 raise ValueError("PhysToVirt does not support {0}".format(self.arch)) 513 514 @cache_statically 515 def GetUsecDivisor(self, target=None): 516 if self.arch == 'x86_64': 517 return 1000 518 519 rtclockdata_addr = self.GetLoadAddressForSymbol('RTClockData') 520 rtc = self.GetValueFromAddress(rtclockdata_addr, 'struct _rtclock_data_ *') 521 return unsigned(rtc.rtc_usec_divisor) 522 523 def GetNanotimeFromAbstime(self, abstime): 524 """ convert absolute time (which is in MATUs) to nano seconds. 525 Since based on architecture the conversion may differ. 526 params: 527 abstime - int absolute time as shown by mach_absolute_time 528 returns: 529 int - nanosecs of time 530 """ 531 return (abstime * 1000) // self.GetUsecDivisor() 532 533 @property 534 @cache_statically 535 def zones(self, target=None): 536 za = target.chkFindFirstGlobalVariable('zone_array') 537 zs = target.chkFindFirstGlobalVariable('zone_security_array') 538 n = target.chkFindFirstGlobalVariable('num_zones').xGetValueAsInteger() 539 540 iter_za = za.chkGetChildAtIndex(0).xIterSiblings(0, n) 541 iter_zs = zs.chkGetChildAtIndex(0).xIterSiblings(0, n) 542 543 return [ 544 (value(next(iter_za).AddressOf()), value(next(iter_zs).AddressOf())) 545 for i in range(n) 546 ] 547 548 @property 549 def threads(self): 550 target = LazyTarget.GetTarget() 551 552 return (value(t.AddressOf()) for t in ccol.iter_queue( 553 target.chkFindFirstGlobalVariable('threads'), 554 gettype('thread'), 555 'threads', 556 )) 557 558 @dyn_cached_property 559 def tasks(self, target=None): 560 return [value(t.AddressOf()) for t in ccol.iter_queue( 561 target.chkFindFirstGlobalVariable('tasks'), 562 gettype('task'), 563 'tasks', 564 )] 565 566 @property 567 def coalitions(self): 568 target = LazyTarget.GetTarget() 569 570 return (value(coal.AddressOf()) for coal in ccol.SMRHash( 571 target.chkFindFirstGlobalVariable('coalition_hash'), 572 target.chkFindFirstGlobalVariable('coal_hash_traits'), 573 )) 574 575 @property 576 def thread_groups(self): 577 target = LazyTarget.GetTarget() 578 579 return (value(tg.AddressOf()) for tg in ccol.iter_queue_entries( 580 target.chkFindFirstGlobalVariable('tg_queue'), 581 gettype('thread_group'), 582 'tg_queue_chain', 583 )) 584 585 @property 586 def terminated_tasks(self): 587 target = LazyTarget.GetTarget() 588 589 return (value(t.AddressOf()) for t in ccol.iter_queue( 590 target.chkFindFirstGlobalVariable('terminated_tasks'), 591 gettype('task'), 592 'tasks', 593 )) 594 595 @property 596 def terminated_threads(self): 597 target = LazyTarget.GetTarget() 598 599 return (value(t.AddressOf()) for t in ccol.iter_queue( 600 target.chkFindFirstGlobalVariable('terminated_threads'), 601 gettype('thread'), 602 'threads', 603 )) 604 605 @property 606 def procs(self): 607 target = LazyTarget.GetTarget() 608 609 return (value(p.AddressOf()) for p in ccol.iter_LIST_HEAD( 610 target.chkFindFirstGlobalVariable('allproc'), 611 'p_list', 612 )) 613 614 @property 615 def interrupt_stats(self): 616 target = LazyTarget.GetTarget() 617 618 return (value(stat.AddressOf()) for stat in ccol.iter_queue( 619 target.chkFindFirstGlobalVariable('gInterruptAccountingDataList'), 620 gettype('IOInterruptAccountingData'), 621 'chain', 622 )) 623 624 @property 625 def zombprocs(self): 626 target = LazyTarget.GetTarget() 627 628 return (value(p.AddressOf()) for p in ccol.iter_LIST_HEAD( 629 target.chkFindFirstGlobalVariable('zombproc'), 630 'p_list', 631 )) 632 633 @property 634 def version(self): 635 return str(self.globals.version) 636 637 @property 638 def arch(self): 639 return LazyTarget.GetTarget().triple.split('-', 1)[0] 640 641 @property 642 def ptrsize(self): 643 return LazyTarget.GetTarget().GetAddressByteSize() 644 645 @property 646 def VM_MIN_KERNEL_ADDRESS(self): 647 if self.arch == 'x86_64': 648 return 0xffffff8000000000 649 else: 650 return 0xffffffe00000000 651 652 @property 653 def VM_MIN_KERNEL_AND_KEXT_ADDRESS(self): 654 if self.arch == 'x86_64': 655 return 0xffffff8000000000 - 0x80000000 656 else: 657 return 0xffffffe00000000 658