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