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 collections 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, type_str = 'void *'): 373 """ convert a address to 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 obj = value(self.globals.version.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr))) 381 obj = cast(obj, type_str) 382 return obj 383 384 def CreateTypedPointerFromAddress(self, addr, type_str = "char"): 385 """ convert a address to pointer value 386 387 Note: This is obsolete and here as a temporary solution 388 for people to migrate to using references instead. 389 390 params: 391 addr - int : typically hex value like 0xffffff80008dc390 392 type_str - str: type to cast to, must not be a pointer type. 393 returns: 394 value : a value object which has address as addr 395 and type is `type_str *` 396 """ 397 398 target = LazyTarget.GetTarget() 399 sbv = target.xCreateValueFromAddress(None, addr, gettype(type_str)) 400 return value(sbv.AddressOf()) 401 402 403 def GetValueAsType(self, v, t): 404 """ Retrieves a global variable 'v' of type 't' wrapped in a vue object. 405 If 'v' is an address, creates a vue object of the appropriate type. 406 If 'v' is a name, looks for the global variable and asserts its type. 407 Throws: 408 NameError - If 'v' cannot be found 409 TypeError - If 'v' is of the wrong type 410 """ 411 if islong(v): 412 return self.GetValueFromAddress(v, t) 413 else: 414 var = LazyTarget.GetTarget().FindGlobalVariables(v, 1)[0] 415 if not var: 416 raise NameError("Failed to find global variable '{0}'".format(v)) 417 if var.GetTypeName() != t: 418 raise TypeError("{0} must be of type '{1}', not '{2}'".format(v, t, var.GetTypeName())) 419 return value(var) 420 421 def _GetIterator(self, iter_head_name, next_element_name='next', iter_head_type=None): 422 """ returns an iterator for a collection in kernel memory. 423 params: 424 iter_head_name - str : name of queue_head or list head variable. 425 next_element_name - str : name of the element that leads to next element. 426 for ex. in struct zone list 'next_zone' is the linking element. 427 returns: 428 iterable : typically used in conjunction with "for varname in iterable:" 429 """ 430 head_element = self.GetGlobalVariable(iter_head_name) 431 return head_element.GetSBValue().linked_list_iter(next_element_name) 432 433 def TruncPage(self, addr): 434 return (addr & ~(unsigned(self.GetGlobalVariable("page_size")) - 1)) 435 436 def RoundPage(self, addr): 437 return trunc_page(addr + unsigned(self.GetGlobalVariable("page_size")) - 1) 438 439 def StraddlesPage(self, addr, size): 440 if size > unsigned(self.GetGlobalVariable("page_size")): 441 return True 442 val = ((addr + size) & (unsigned(self.GetGlobalVariable("page_size"))-1)) 443 return (val < size and val > 0) 444 445 def StripUserPAC(self, addr): 446 if self.arch != 'arm64e': 447 return addr 448 T0Sz = self.GetGlobalVariable('gT0Sz') 449 return StripPAC(addr, T0Sz) 450 451 def StripKernelPAC(self, addr): 452 if self.arch != 'arm64e': 453 return addr 454 T1Sz = self.GetGlobalVariable('gT1Sz') 455 return StripPAC(addr, T1Sz) 456 457 PAGE_PROTECTION_TYPE_NONE = 0 458 PAGE_PROTECTION_TYPE_PPL = 1 459 PAGE_PROTECTION_TYPE_SPTM = 2 460 461 def PhysToKVARM64(self, addr): 462 if self.globals.page_protection_type <= self.PAGE_PROTECTION_TYPE_PPL: 463 ptov_table = self.globals.ptov_table 464 for i in range(0, self.globals.ptov_index): 465 if (addr >= int(unsigned(ptov_table[i].pa))) and (addr < (int(unsigned(ptov_table[i].pa)) + int(unsigned(ptov_table[i].len)))): 466 return (addr - int(unsigned(ptov_table[i].pa)) + int(unsigned(ptov_table[i].va))) 467 else: 468 papt_table = self.globals.libsptm_papt_ranges 469 page_size = self.globals.page_size 470 for i in range(0, self.globals.libsptm_n_papt_ranges): 471 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))): 472 return (addr - int(unsigned(papt_table[i].paddr_start)) + int(unsigned(papt_table[i].papt_start))) 473 raise ValueError("PA {:#x} not found in physical region lookup table".format(addr)) 474 return (addr - unsigned(self.globals.gPhysBase) + unsigned(self.globals.gVirtBase)) 475 476 def PhysToKernelVirt(self, addr): 477 if self.arch == 'x86_64': 478 return (addr + unsigned(self.GetGlobalVariable('physmap_base'))) 479 elif self.arch.startswith('arm64'): 480 return self.PhysToKVARM64(addr) 481 elif self.arch.startswith('arm'): 482 return (addr - unsigned(self.GetGlobalVariable("gPhysBase")) + unsigned(self.GetGlobalVariable("gVirtBase"))) 483 else: 484 raise ValueError("PhysToVirt does not support {0}".format(self.arch)) 485 486 @cache_statically 487 def GetUsecDivisor(self, target=None): 488 if self.arch == 'x86_64': 489 return 1000 490 491 rtclockdata_addr = self.GetLoadAddressForSymbol('RTClockData') 492 rtc = self.GetValueFromAddress(rtclockdata_addr, 'struct _rtclock_data_ *') 493 return unsigned(rtc.rtc_usec_divisor) 494 495 def GetNanotimeFromAbstime(self, abstime): 496 """ convert absolute time (which is in MATUs) to nano seconds. 497 Since based on architecture the conversion may differ. 498 params: 499 abstime - int absolute time as shown by mach_absolute_time 500 returns: 501 int - nanosecs of time 502 """ 503 return (abstime * 1000) // self.GetUsecDivisor() 504 505 @property 506 @cache_statically 507 def zones(self, target=None): 508 za = target.chkFindFirstGlobalVariable('zone_array') 509 zs = target.chkFindFirstGlobalVariable('zone_security_array') 510 n = target.chkFindFirstGlobalVariable('num_zones').xGetValueAsInteger() 511 512 iter_za = za.chkGetChildAtIndex(0).xIterSiblings(0, n) 513 iter_zs = zs.chkGetChildAtIndex(0).xIterSiblings(0, n) 514 515 return [ 516 (value(next(iter_za).AddressOf()), value(next(iter_zs).AddressOf())) 517 for i in range(n) 518 ] 519 520 @property 521 def threads(self): 522 target = LazyTarget.GetTarget() 523 524 return (value(t.AddressOf()) for t in ccol.iter_queue( 525 target.chkFindFirstGlobalVariable('threads'), 526 gettype('thread'), 527 'threads', 528 )) 529 530 @dyn_cached_property 531 def tasks(self, target=None): 532 return [value(t.AddressOf()) for t in ccol.iter_queue( 533 target.chkFindFirstGlobalVariable('tasks'), 534 gettype('task'), 535 'tasks', 536 )] 537 538 @property 539 def coalitions(self): 540 target = LazyTarget.GetTarget() 541 542 return (value(coal.AddressOf()) for coal in ccol.SMRHash( 543 target.chkFindFirstGlobalVariable('coalition_hash'), 544 target.chkFindFirstGlobalVariable('coal_hash_traits'), 545 )) 546 547 @property 548 def thread_groups(self): 549 target = LazyTarget.GetTarget() 550 551 return (value(tg.AddressOf()) for tg in ccol.iter_queue_entries( 552 target.chkFindFirstGlobalVariable('tg_queue'), 553 gettype('thread_group'), 554 'tg_queue_chain', 555 )) 556 557 @property 558 def terminated_tasks(self): 559 target = LazyTarget.GetTarget() 560 561 return (value(t.AddressOf()) for t in ccol.iter_queue( 562 target.chkFindFirstGlobalVariable('terminated_tasks'), 563 gettype('task'), 564 'tasks', 565 )) 566 567 @property 568 def terminated_threads(self): 569 target = LazyTarget.GetTarget() 570 571 return (value(t.AddressOf()) for t in ccol.iter_queue( 572 target.chkFindFirstGlobalVariable('terminated_threads'), 573 gettype('thread'), 574 'threads', 575 )) 576 577 @property 578 def procs(self): 579 target = LazyTarget.GetTarget() 580 581 return (value(p.AddressOf()) for p in ccol.iter_LIST_HEAD( 582 target.chkFindFirstGlobalVariable('allproc'), 583 'p_list', 584 )) 585 586 @property 587 def interrupt_stats(self): 588 target = LazyTarget.GetTarget() 589 590 return (value(stat.AddressOf()) for stat in ccol.iter_queue( 591 target.chkFindFirstGlobalVariable('gInterruptAccountingDataList'), 592 gettype('IOInterruptAccountingData'), 593 'chain', 594 )) 595 596 @property 597 def zombprocs(self): 598 target = LazyTarget.GetTarget() 599 600 return (value(p.AddressOf()) for p in ccol.iter_LIST_HEAD( 601 target.chkFindFirstGlobalVariable('zombproc'), 602 'p_list', 603 )) 604 605 @property 606 def version(self): 607 return str(self.globals.version) 608 609 @property 610 def arch(self): 611 return LazyTarget.GetTarget().triple.split('-', 1)[0] 612 613 @property 614 def ptrsize(self): 615 return LazyTarget.GetTarget().GetAddressByteSize() 616 617 @property 618 def VM_MIN_KERNEL_ADDRESS(self): 619 if self.arch == 'x86_64': 620 return 0xffffff8000000000 621 else: 622 return 0xffffffe00000000 623 624 @property 625 def VM_MIN_KERNEL_AND_KEXT_ADDRESS(self): 626 if self.arch == 'x86_64': 627 return 0xffffff8000000000 - 0x80000000 628 else: 629 return 0xffffffe00000000 630