1from xnu import * 2from utils import * 3from kdp import * 4from core import caching 5from core.pointer import NativePointer 6import sys 7import lldb 8import os 9import sys 10from collections import deque 11 12###################################### 13# Globals 14###################################### 15plane = None 16 17##################################### 18# Utility functions. 19##################################### 20def CastIOKitClass(obj, target_type): 21 """ Type cast an object to another IOKIT CPP class. 22 params: 23 obj - core.value object representing some C construct in lldb 24 target_type - str : ex 'OSString *' 25 - lldb.SBType : 26 """ 27 v = obj.GetSBValue() 28 # We need to do that so that LLDB doesn't try to "helpfully" 29 # Guess which instance type it is... 30 v.SetPreferDynamicValue(lldb.eNoDynamicValues) 31 if isinstance(target_type, str): 32 target_type = gettype(target_type) 33 return value(v.Cast(target_type)) 34 35##################################### 36# Classes. 37##################################### 38class PreoslogHeader(object): 39 """ 40 Represents preoslog buffer header. There's no symbol in the kernel for it. 41 """ 42 valid_magic = "POSL" 43 def __init__(self): 44 self.magic = "" 45 self.offset = 0 46 self.size = 0 47 self.source = 0 48 self.wrapped = 0 49 self.data = None 50 51 52class IOKitSmartPointer(NativePointer): 53 """ IOKit's smart pointer 54 55 Every smart pointer inherits from libkern::intrusive_shared_ptr. 56 The real pointer is wrapped behind ptr_ member. 57 """ 58 59 @classmethod 60 def match(cls, sbvalue): 61 62 # Smart pointers in IOKit are OSSharedPtr and OSTaggedSharedPtr 63 name = sbvalue.GetType().GetCanonicalType().GetName() 64 if name.startswith(("OSSharedPtr", "OSTaggedSharedPtr")): 65 return cls() 66 67 return None 68 69 def GetPointerSBValue(self, sbvalue): 70 sbv = sbvalue.GetChildMemberWithName('ptr_') 71 return super().GetPointerSBValue(sbv) 72 73 74###################################### 75# Type Summaries 76###################################### 77@lldb_type_summary(['OSObject *']) 78@header("") 79def GetObjectSummary(obj): 80 """ Show info about an OSObject - its vtable ptr and retain count, & more info for simple container classes. 81 """ 82 if obj is None: 83 return 84 85 vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t') 86 vt = kern.StripKernelPAC(vt) 87 vtype = kern.SymbolicateFromAddress(vt) 88 if len(vtype): 89 vtype_str = " <" + vtype[0].GetName() + ">" 90 else: 91 vtype_str = "" 92 if (retainCount := get_field(obj, 'retainCount')) is not None: 93 retCount = (retainCount & 0xffff) 94 cntnrRetCount = (retainCount >> 16) 95 out_string = "`object 0x{0: <16x}, vt 0x{1: <16x}{2:s}, retain count {3:d}, container retain {4:d}` ".format(obj, vt, vtype_str, retCount, cntnrRetCount) 96 else: 97 out_string = "`object 0x{0: <16x}, vt 0x{1: <16x}{2:s}` ".format(obj, vt, vtype_str) 98 99 ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSString') 100 if vt == ztvAddr: 101 out_string += GetString(obj) 102 return out_string 103 104 ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSSymbol') 105 if vt == ztvAddr: 106 out_string += GetString(obj) 107 return out_string 108 109 ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSNumber') 110 if vt == ztvAddr: 111 out_string += GetNumber(obj) 112 return out_string 113 114 ztvAddr = kern.GetLoadAddressForSymbol('_ZTV9OSBoolean') 115 if vt == ztvAddr: 116 out_string += GetBoolean(obj) 117 return out_string 118 119 ztvAddr = kern.GetLoadAddressForSymbol('_ZTV7OSArray') 120 if vt == ztvAddr: 121 out_string += "(" + GetArray(CastIOKitClass(obj, 'OSArray *')) + ")" 122 return out_string 123 124 ztvAddr = kern.GetLoadAddressForSymbol('_ZTV5OSSet') 125 if vt == ztvAddr: 126 out_string += GetSet(CastIOKitClass(obj, 'OSSet *')) 127 return out_string 128 129 ztvAddr = kern.GetLoadAddressForSymbol('_ZTV12OSDictionary') 130 if vt == ztvAddr: 131 out_string += GetDictionary(CastIOKitClass(obj, 'OSDictionary *')) 132 return out_string 133 134 return out_string 135 136 137def GetObjectTypeStr(obj): 138 """ Return the type of an OSObject's container class 139 """ 140 if obj is None: 141 return None 142 143 vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t') 144 vt = kern.StripKernelPAC(vt) 145 vtype = kern.SymbolicateFromAddress(vt) 146 if len(vtype): 147 return vtype[0].GetName() 148 149 # See if the value is in a kext with no symbols 150 for kval in IterateLinkedList(kern.globals.kmod, 'next'): 151 if vt >= unsigned(kval.address) and vt <= (unsigned(kval.address) + unsigned(kval.size)): 152 return "kmod:{:s}+{:#0x}".format(kval.name, vt - unsigned(kval.address)) 153 return None 154 155 156@lldb_type_summary(['IORegistryEntry *']) 157@header("") 158def GetRegistryEntrySummary(entry): 159 """ returns a string containing summary information about an IORegistry 160 object including it's registry id , vtable ptr and retain count 161 """ 162 name = None 163 out_string = "" 164 registryTable = entry.fRegistryTable 165 propertyTable = entry.fPropertyTable 166 167 name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) 168 if name is None: 169 name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) 170 if name is None: 171 name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) 172 173 if name is not None: 174 out_string += "+-o {0:s} ".format(GetString(CastIOKitClass(name, 'OSString *'))) 175 elif (pwrMgt := CastIOKitClass(entry, 'IOService *').pwrMgt) and (service_name := pwrMgt.Name): 176 out_string += "+-o {0:s} ".format(service_name) 177 else: 178 out_string += "+-o ?? " 179 180 # I'm using uintptr_t for now to work around <rdar://problem/12749733> FindFirstType & Co. should allow you to make pointer types directly 181 vtableAddr = dereference(Cast(entry, 'uintptr_t *')) - 2 * sizeof('uintptr_t *') 182 vtableAddr = kern.StripKernelPAC(vtableAddr) 183 vtype = kern.SymbolicateFromAddress(vtableAddr) 184 if vtype is None or len(vtype) < 1: 185 out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x}".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID, vtableAddr) 186 else: 187 out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x} <{3:s}>".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID, 188 vtableAddr, vtype[0].GetName()) 189 190 ztvAddr = kern.GetLoadAddressForSymbol('_ZTV15IORegistryEntry') 191 if vtableAddr != ztvAddr: 192 out_string += ", " 193 state = CastIOKitClass(entry, 'IOService *').__state[0] 194 # kIOServiceRegisteredState 195 if 0 == state & 2: 196 out_string += "!" 197 out_string += "registered, " 198 # kIOServiceMatchedState 199 if 0 == state & 4: 200 out_string += "!" 201 out_string += "matched, " 202 #kIOServiceInactiveState 203 if 0 != state & 1: 204 out_string += "in" 205 busyCount = (CastIOKitClass(entry, 'IOService *').__state[1] & 0xff) 206 retCount = (CastIOKitClass(entry, 'IOService *').retainCount & 0xffff) 207 out_string += "active, busy {0}, retain count {1}>".format(busyCount, retCount) 208 return out_string 209 210###################################### 211# Commands 212###################################### 213@lldb_command('showallclasses') 214def ShowAllClasses(cmd_args=None): 215 """ Show the instance counts and ivar size of all OSObject subclasses. 216 See ioclasscount man page for details 217 """ 218 idx = 0 219 count = unsigned(kern.globals.sAllClassesDict.count) 220 221 while idx < count: 222 meta = CastIOKitClass(kern.globals.sAllClassesDict.dictionary[idx].value, 'OSMetaClass *') 223 idx += 1 224 print(GetMetaClass(meta)) 225 226@lldb_command('showobject') 227def ShowObject(cmd_args=None): 228 """ Show info about an OSObject - its vtable ptr and retain count, & more info for simple container classes. 229 """ 230 if cmd_args is None or len(cmd_args) == 0: 231 raise ArgumentError("Please specify the address of the OSObject whose info you want to view. Type help showobject for help") 232 return 233 234 obj = kern.GetValueFromAddress(cmd_args[0], 'OSObject *') 235 print(GetObjectSummary(obj)) 236 237#Macro: dumpobject 238@lldb_command('dumpobject') 239def DumpObject(cmd_args=None): 240 """ Dumps object information if it is a valid object confirmed by showobject 241 Usage: dumpobject <address of object to be dumped> [class/struct type of object] 242 """ 243 if cmd_args is None or len(cmd_args) == 0: 244 raise ArgumentError("No arguments passed") 245 return False 246 247 if len(cmd_args) == 1: 248 try: 249 object_info = lldb_run_command("showobject {:s}".format(cmd_args[0])) 250 except: 251 print("Error!! showobject failed due to invalid value") 252 print(DumpObject.__doc__) 253 return False 254 255 srch = re.search(r'<vtable for ([A-Za-z][^>]*)>', object_info) 256 if not srch: 257 print("Error!! Couldn't find object in registry, input type manually as 2nd argument") 258 print(DumpObject.__doc__) 259 return False 260 261 object_type = srch.group(1) 262 else: 263 type_lookup = lldb_run_command("image lookup -t {:s}".format(cmd_args[1])) 264 if type_lookup.find(cmd_args[1])!= -1: 265 object_type = cmd_args[1] 266 else: 267 print("Error!! Input type {:s} isn't available in image lookup".format(cmd_args[1])) 268 return False 269 270 print("******** Object Dump for value \'{:s}\' with type \"{:s}\" ********".format(cmd_args[0], object_type)) 271 print(lldb_run_command("p/x *({:s}*){:s}".format(object_type, cmd_args[0]))) 272 273#EndMacro: dumpobject 274 275@lldb_command('setregistryplane') 276def SetRegistryPlane(cmd_args=None): 277 """ Set the plane to be used for the IOKit registry macros 278 syntax: (lldb) setregistryplane 0 - will display all known planes 279 syntax: (lldb) setregistryplane 0xaddr - will set the registry plane to 0xaddr 280 syntax: (lldb) setregistryplane gIODTPlane - will set the registry plane to gIODTPlane 281 """ 282 if cmd_args is None or len(cmd_args) == 0: 283 raise ArgumentError("Please specify the name of the plane you want to use with the IOKit registry macros.") 284 285 if cmd_args[0] == "0": 286 print(GetObjectSummary(kern.globals.gIORegistryPlanes)) 287 else: 288 global plane 289 plane = kern.GetValueFromAddress(cmd_args[0], 'IORegistryPlane *') 290 return 291 292@lldb_command('showregistryentry') 293def ShowRegistryEntry(cmd_args=None): 294 """ Show info about a registry entry; its properties and descendants in the current plane 295 syntax: (lldb) showregistryentry 0xaddr 296 syntax: (lldb) showregistryentry gIOPMRootDomain 297 """ 298 if cmd_args is None or len(cmd_args) == 0: 299 raise ArgumentError("Please specify the address of the registry entry whose info you want to view.") 300 return 301 302 entry = kern.GetValueFromAddress(cmd_args[0], 'IORegistryEntry *') 303 ShowRegistryEntryRecurse(entry, "", True) 304 305@lldb_command('showregistry') 306def ShowRegistry(cmd_args=None): 307 """ Show info about all registry entries in the current plane 308 If prior to invoking this command no registry plane is specified 309 using 'setregistryplane', the command defaults to the IOService plane 310 """ 311 ShowRegistryEntryRecurse(kern.globals.gRegistryRoot, "", False) 312 313@lldb_command('showregistryprops') 314def ShowRegistryProps(cmd_args=None): 315 """ Show info about all registry entries in the current plane, and their properties 316 If prior to invoking this command no registry plane is specified 317 using 'setregistryplane', the command defaults to the IOService plane 318 """ 319 ShowRegistryEntryRecurse(kern.globals.gRegistryRoot, "", True) 320 321@lldb_command('findregistryentry') 322def FindRegistryEntry(cmd_args=None): 323 """ Search for registry entry that matches the given string 324 If prior to invoking this command no registry plane is specified 325 using 'setregistryplane', the command defaults to searching entries from the IOService plane 326 syntax: (lldb) findregistryentries AppleACPICPU - will find the first registry entry that matches AppleACPICPU 327 """ 328 if cmd_args is None or len(cmd_args) == 0: 329 raise ArgumentError("Please specify the name of the registry entry you want to find") 330 331 FindRegistryEntryRecurse(kern.globals.gRegistryRoot, cmd_args[0], True) 332 333@lldb_command('findregistryentries') 334def FindRegistryEntries(cmd_args=None): 335 """ Search for all registry entries that match the given string 336 If prior to invoking this command no registry plane is specified 337 using 'setregistryplane', the command defaults to searching entries from the IOService plane 338 syntax: (lldb) findregistryentries AppleACPICPU - will find all registry entries that match AppleACPICPU 339 """ 340 if cmd_args is None or len(cmd_args) == 0: 341 raise ArgumentError("Please specify the name of the registry entry/entries you want to find") 342 343 FindRegistryEntryRecurse(kern.globals.gRegistryRoot, cmd_args[0], False) 344 345@lldb_command('findregistryprop') 346def FindRegistryProp(cmd_args=None): 347 """ Given a registry entry, print out the contents for the property that matches 348 a specific string 349 syntax: (lldb) findregistryprop 0xaddr IOSleepSupported 350 syntax: (lldb) findregistryprop gIOPMRootDomain IOSleepSupported 351 syntax: (lldb) findregistryprop gIOPMRootDomain "Supported Features" 352 """ 353 if cmd_args is None or len(cmd_args) < 2: 354 raise ArgumentError("Please specify the address of a IORegistry entry and the property you're looking for") 355 356 entry = kern.GetValueFromAddress(cmd_args[0], 'IOService *') 357 propertyTable = entry.fPropertyTable 358 print(GetObjectSummary(LookupKeyInPropTable(propertyTable, cmd_args[1]))) 359 360@lldb_command('showuserserver') 361def ShowUserServer(cmd_args=None): 362 """ Show info about an IOUserServer object 363 syntax: (lldb) showuserserver 0xaddr 364 """ 365 if cmd_args is None or len(cmd_args) == 0: 366 raise ArgumentError("Please specify the address of the IOUserServer object whose info you want to view.") 367 return 368 ShowUserServerSummary(cmd_args[0]) 369 370@lldb_command('readioport8') 371def ReadIOPort8(cmd_args=None): 372 """ Read value stored in the specified IO port. The CPU can be optionally 373 specified as well. 374 Prints 0xBAD10AD in case of a bad read 375 Syntax: (lldb) readioport8 <port> [lcpu (kernel's numbering convention)] 376 """ 377 if cmd_args is None or len(cmd_args) == 0: 378 raise ArgumentError("Please specify a port to read out of") 379 380 portAddr = ArgumentStringToInt(cmd_args[0]) 381 if len(cmd_args) >= 2: 382 lcpu = ArgumentStringToInt(cmd_args[1]) 383 else: 384 lcpu = xnudefines.lcpu_self 385 386 ReadIOPortInt(portAddr, 1, lcpu) 387 388@lldb_command('readioport16') 389def ReadIOPort16(cmd_args=None): 390 """ Read value stored in the specified IO port. The CPU can be optionally 391 specified as well. 392 Prints 0xBAD10AD in case of a bad read 393 Syntax: (lldb) readioport16 <port> [lcpu (kernel's numbering convention)] 394 """ 395 if cmd_args is None or len(cmd_args) == 0: 396 raise ArgumentError("Please specify a port to read out of") 397 398 portAddr = ArgumentStringToInt(cmd_args[0]) 399 if len(cmd_args) >= 2: 400 lcpu = ArgumentStringToInt(cmd_args[1]) 401 else: 402 lcpu = xnudefines.lcpu_self 403 404 ReadIOPortInt(portAddr, 2, lcpu) 405 406@lldb_command('readioport32') 407def ReadIOPort32(cmd_args=None): 408 """ Read value stored in the specified IO port. The CPU can be optionally 409 specified as well. 410 Prints 0xBAD10AD in case of a bad read 411 Syntax: (lldb) readioport32 <port> [lcpu (kernel's numbering convention)] 412 """ 413 if cmd_args is None or len(cmd_args) == 0: 414 raise ArgumentError("Please specify a port to read out of") 415 416 portAddr = ArgumentStringToInt(cmd_args[0]) 417 if len(cmd_args) >= 2: 418 lcpu = ArgumentStringToInt(cmd_args[1]) 419 else: 420 lcpu = xnudefines.lcpu_self 421 422 ReadIOPortInt(portAddr, 4, lcpu) 423 424@lldb_command('writeioport8') 425def WriteIOPort8(cmd_args=None): 426 """ Write the value to the specified IO port. The size of the value is 427 determined by the name of the command. The CPU used can be optionally 428 specified as well. 429 Syntax: (lldb) writeioport8 <port> <value> [lcpu (kernel's numbering convention)] 430 """ 431 if cmd_args is None or len(cmd_args) < 2: 432 raise ArgumentError("Please specify a port to write to, followed by the value you want to write") 433 434 portAddr = ArgumentStringToInt(cmd_args[0]) 435 value = ArgumentStringToInt(cmd_args[1]) 436 437 if len(cmd_args) >= 3: 438 lcpu = ArgumentStringToInt(cmd_args[2]) 439 else: 440 lcpu = xnudefines.lcpu_self 441 442 WriteIOPortInt(portAddr, 1, value, lcpu) 443 444@lldb_command('writeioport16') 445def WriteIOPort16(cmd_args=None): 446 """ Write the value to the specified IO port. The size of the value is 447 determined by the name of the command. The CPU used can be optionally 448 specified as well. 449 Syntax: (lldb) writeioport16 <port> <value> [lcpu (kernel's numbering convention)] 450 """ 451 if cmd_args is None or len(cmd_args) < 2: 452 raise ArgumentError("Please specify a port to write to, followed by the value you want to write") 453 454 portAddr = ArgumentStringToInt(cmd_args[0]) 455 value = ArgumentStringToInt(cmd_args[1]) 456 457 if len(cmd_args) >= 3: 458 lcpu = ArgumentStringToInt(cmd_args[2]) 459 else: 460 lcpu = xnudefines.lcpu_self 461 462 WriteIOPortInt(portAddr, 2, value, lcpu) 463 464@lldb_command('writeioport32') 465def WriteIOPort32(cmd_args=None): 466 """ Write the value to the specified IO port. The size of the value is 467 determined by the name of the command. The CPU used can be optionally 468 specified as well. 469 Syntax: (lldb) writeioport32 <port> <value> [lcpu (kernel's numbering convention)] 470 """ 471 if cmd_args is None or len(cmd_args) < 2: 472 raise ArgumentError("Please specify a port to write to, followed by the value you want to write") 473 474 portAddr = ArgumentStringToInt(cmd_args[0]) 475 value = ArgumentStringToInt(cmd_args[1]) 476 477 if len(cmd_args) >= 3: 478 lcpu = ArgumentStringToInt(cmd_args[2]) 479 else: 480 lcpu = xnudefines.lcpu_self 481 482 WriteIOPortInt(portAddr, 4, value, lcpu) 483 484@lldb_command('showioservicepm') 485def ShowIOServicePM(cmd_args=None): 486 """ Routine to dump the IOServicePM object 487 Syntax: (lldb) showioservicepm <IOServicePM pointer> 488 """ 489 if cmd_args is None or len(cmd_args) == 0: 490 raise ArgumentError("Please enter the pointer to the IOServicePM object you'd like to introspect") 491 492 iopmpriv = kern.GetValueFromAddress(cmd_args[0], 'IOServicePM *') 493 out_string = "MachineState {0: <6d} (".format(iopmpriv.MachineState) 494 495 # Power state map 496 pstate_map = { 497 0: 'kIOPM_Finished', 498 1: 'kIOPM_OurChangeTellClientsPowerDown', 499 2: 'kIOPM_OurChangeTellClientsPowerDown', 500 3: 'kIOPM_OurChangeNotifyInterestedDriversWillChange', 501 4: 'kIOPM_OurChangeSetPowerState', 502 5: 'kIOPM_OurChangeWaitForPowerSettle', 503 6: 'kIOPM_OurChangeNotifyInterestedDriversDidChange', 504 7: 'kIOPM_OurChangeTellCapabilityDidChange', 505 8: 'kIOPM_OurChangeFinish', 506 9: 'Unused_MachineState_9', 507 10: 'kIOPM_ParentChangeTellPriorityClientsPowerDown', 508 11: 'kIOPM_ParentChangeNotifyInterestedDriversWillChange', 509 12: 'kIOPM_ParentChangeSetPowerState', 510 13: 'kIOPM_ParentChangeWaitForPowerSettle', 511 14: 'kIOPM_ParentChangeNotifyInterestedDriversDidChange', 512 15: 'kIOPM_ParentChangeTellCapabilityDidChange', 513 16: 'kIOPM_ParentChangeAcknowledgePowerChange', 514 17: 'kIOPM_NotifyChildrenStart', 515 18: 'kIOPM_NotifyChildrenOrdered', 516 19: 'kIOPM_NotifyChildrenDelayed', 517 20: 'kIOPM_SyncTellClientsPowerDown', 518 21: 'kIOPM_SyncTellPriorityClientsPowerDown', 519 22: 'kIOPM_SyncNotifyWillChange', 520 23: 'kIOPM_SyncNotifyDidChange', 521 24: 'kIOPM_SyncTellCapabilityDidChange', 522 25: 'kIOPM_SyncFinish', 523 26: 'kIOPM_TellCapabilityChangeDone', 524 27: 'kIOPM_DriverThreadCallDone' 525 } 526 powerstate = unsigned(iopmpriv.MachineState) 527 if powerstate in pstate_map: 528 out_string += "{0:s}".format(pstate_map[powerstate]) 529 else: 530 out_string += "Unknown_MachineState" 531 out_string += "), " 532 533 if iopmpriv.MachineState != 20: 534 if hasattr(iopmpriv, "SettleTimeUS"): 535 out_string += "DriverTimer = {0: <6d}, SettleTime = {1: < 6d}, HeadNoteFlags = {2: #12x}, HeadNotePendingAcks = {3: #012x}, ".format( 536 unsigned(iopmpriv.DriverTimer), 537 unsigned(iopmpriv.SettleTimeUS), 538 unsigned(iopmpriv.HeadNoteChangeFlags), 539 unsigned(iopmpriv.HeadNotePendingAcks)) 540 else: 541 out_string += "DriverTimer = {0: <6d}, HeadNoteFlags = {1: #12x}, HeadNotePendingAcks = {2: #012x}, ".format( 542 unsigned(iopmpriv.DriverTimer), 543 unsigned(iopmpriv.HeadNoteChangeFlags), 544 unsigned(iopmpriv.HeadNotePendingAcks)) 545 546 if iopmpriv.DeviceOverrideEnabled != 0: 547 out_string += "DeviceOverrides, " 548 549 out_string += "DeviceDesire = {0: <6d}, DesiredPowerState = {1: <6d}, PreviousRequest = {2: <6d}\n".format( 550 unsigned(iopmpriv.DeviceDesire), 551 unsigned(iopmpriv.DesiredPowerState), 552 unsigned(iopmpriv.PreviousRequestPowerFlags)) 553 554 print(out_string) 555 556@lldb_type_summary(['IOPMWorkQueue *']) 557@header("") 558def GetIOPMWorkQueueSummary(wq): 559 out_str = "" 560 ioservicepm_header = "{:<20s}{:<4s}{:<4s}{:<4s}{:<4s}\n" 561 iopmrequest_indent = " " 562 iopmrequest_header = iopmrequest_indent + "{:<20s}{:<6s}{:<20s}{:<20s}{:<12s}{:<12s}{:<20s}{:<20s}{:<20s}\n" 563 head = kern.StripKernelPAC(addressof(wq.fWorkQueue)) 564 head = kern.GetValueFromAddress(head, 'queue_head_t *') 565 566 for next in IterateQueue(head, 'IOServicePM *', 'WorkChain'): 567 out_str += ioservicepm_header.format("IOService", "ps", "ms", "wr", "name") 568 out_str += "0x{:<16x} {:<2d} {:<2d} {:<2d} {:<s}\n".format( 569 next.Owner, next.CurrentPowerState, next.MachineState, next.WaitReason, next.Name) 570 out_str += iopmrequest_header.format("IOPMRequest", "type", "next_req", "root_req", "work_wait", "free_wait", "arg0", "arg1", "arg2") 571 next_head = kern.StripKernelPAC(addressof(next.RequestHead)) 572 next_head = kern.GetValueFromAddress(next_head, 'queue_head_t *') 573 for request in IterateQueue(next_head, 'IOPMRequest *', 'fCommandChain'): 574 out_str += iopmrequest_indent 575 out_str += "0x{:<16x} 0x{:<2x} 0x{:<16x} 0x{:<16x}".format( 576 request, request.fRequestType, request.fRequestNext, request.fRequestRoot) 577 out_str += " 0x{:<8x} 0x{:<8x}".format( 578 request.fWorkWaitCount, request.fFreeWaitCount) 579 out_str += " 0x{:<16x} 0x{:<16x} 0x{:<16x}\n".format( 580 request.fArg0, request.fArg1, request.fArg2) 581 return out_str 582 583@lldb_command('showiopmqueues') 584def ShowIOPMQueues(cmd_args=None): 585 """ Show IOKit power management queues and IOPMRequest objects. 586 """ 587 print("IOPMWorkQueue 0x{:<16x} ({:<d} IOServicePM)\n".format( 588 kern.globals.gIOPMWorkQueue, kern.globals.gIOPMWorkQueue.fQueueLength)) 589 print(GetIOPMWorkQueueSummary(kern.globals.gIOPMWorkQueue)) 590 591@lldb_command('showiouserserverpm') 592def ShowIOUserServerPM(cmd_args=None): 593 """ Show pending power requests managed by IOUserServer instances. 594 """ 595 pendingServers = kern.globals.fUserServersWait 596 count = int(pendingServers.count) 597 if count == 0: 598 print("No user servers with pending power request found") 599 return 600 for idx in range(count): 601 server = CastIOKitClass(pendingServers.array[idx], "IOUserServer *") 602 print(f"IOUserServer: {hex(server)}") 603 services = server.fServices 604 serviceCount = services.count 605 services = services.array 606 for serviceIdx in range(serviceCount): 607 service = CastIOKitClass(services[serviceIdx], "IOService *") 608 uvars = service.reserved.uvars 609 powerState = uvars.powerState 610 pmPending = int(powerState) 611 if pmPending == 0: 612 continue 613 # blatantly copied from GetRegistryEntrySummary 614 name = None 615 registryTable = service.fRegistryTable 616 propertyTable = service.fPropertyTable 617 name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) 618 if name is None: 619 name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) 620 if name is None: 621 name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) 622 name = GetString(CastIOKitClass(name, 'OSString *')) 623 print(f"{name}: {hex(service)}") 624 print("") 625 626@lldb_type_summary(['IOService *']) 627@header("") 628def GetIOPMInterest(service): 629 iopm = CastIOKitClass(service.pwrMgt, 'IOServicePM *') 630 if unsigned(iopm) == 0: 631 raise ArgumentError("error: no IOServicePM") 632 return 633 634 list = CastIOKitClass(iopm.InterestedDrivers, 'IOPMinformeeList *') 635 out_str = "IOServicePM 0x{:<16x} ({:<d} interest, {:<d} pending ack)\n".format( 636 iopm, list.length, iopm.HeadNotePendingAcks) 637 if list.length == 0: 638 return 639 640 out_str += " {:<20s}{:<8s}{:<10s}{:<20s}{:<20s}{:<20s}{:<s}\n".format( 641 "informee", "active", "ticks", "notifyTime", "service", "regId", "name") 642 next = CastIOKitClass(list.firstItem, 'IOPMinformee *') 643 while unsigned(next) != 0: 644 driver = CastIOKitClass(next.whatObject, 'IOService *') 645 name = GetRegistryEntryName(driver) 646 reg_id = CastIOKitClass(driver, 'IORegistryEntry *').reserved.fRegistryEntryID; 647 out_str += " 0x{:<16x} {:<6s} {:<8d} 0x{:<16x} 0x{:<16x} 0x{:<16x} {:<s}\n".format( 648 next, "Yes" if next.active != 0 else "No" , next.timer, next.startTime, next.whatObject, reg_id, name) 649 next = CastIOKitClass(next.nextInList, 'IOPMinformee *') 650 return out_str 651 652@lldb_command('showiopminterest') 653def ShowIOPMInterest(cmd_args=None): 654 """ Show the interested drivers for an IOService. 655 syntax: (lldb) showiopminterest <IOService> 656 """ 657 if cmd_args is None or len(cmd_args) == 0: 658 raise ArgumentError("Please specify the address of the IOService") 659 660 obj = kern.GetValueFromAddress(cmd_args[0], 'IOService *') 661 print(GetIOPMInterest(obj)) 662 663@lldb_command("showinterruptvectors") 664def ShowInterruptVectorInfo(cmd_args=None): 665 """ 666 Shows interrupt vectors. 667 """ 668 669 # Constants 670 kInterruptTriggerModeMask = 0x01 671 kInterruptTriggerModeEdge = 0x00 672 kInterruptTriggerModeLevel = kInterruptTriggerModeMask 673 kInterruptPolarityMask = 0x02 674 kInterruptPolarityHigh = 0x00 675 kInterruptPolarityLow = kInterruptPolarityMask 676 kInterruptShareableMask = 0x04 677 kInterruptNotShareable = 0x00 678 kInterruptIsShareable = kInterruptShareableMask 679 kIOInterruptTypePCIMessaged = 0x00010000 680 681 # Get all interrupt controllers 682 interrupt_controllers = list(SearchInterruptControllerDrivers()) 683 684 print("Interrupt controllers: ") 685 for ic in interrupt_controllers: 686 print(" {}".format(ic)) 687 print("") 688 689 # Iterate over all entries in the registry 690 for entry in GetMatchingEntries(lambda _: True): 691 # Get the name of the entry 692 entry_name = GetRegistryEntryName(entry) 693 694 # Get the location of the entry 695 entry_location = GetRegistryEntryLocationInPlane(entry, kern.globals.gIOServicePlane) 696 if entry_location is None: 697 entry_location = "" 698 else: 699 entry_location = "@" + entry_location 700 701 # Get the interrupt properties 702 (msi_mode, vectorDataList, vectorContList) = GetRegistryEntryInterruptProperties(entry) 703 should_print = False 704 out_str = "" 705 for (vector_data, vector_cont) in zip(vectorDataList, vectorContList): 706 # vector_cont is the name of the interrupt controller. Find the matching controller from 707 # the list of controllers obtained earlier 708 matching_ics = [ic for ic in interrupt_controllers if ic.name == vector_cont] 709 710 if len(matching_ics) > 0: 711 should_print = True 712 # Take the first match 713 matchingIC = matching_ics[0] 714 715 # Use the vector_data to determine the vector and any flags 716 data_ptr = vector_data.data 717 data_length = vector_data.length 718 719 # Dereference vector_data as a uint32_t * and add the base vector number 720 gsi = unsigned(dereference(Cast(data_ptr, 'uint32_t *'))) 721 gsi += matchingIC.base_vector_number 722 723 # If data_length is >= 8 then vector_data contains interrupt flags 724 if data_length >= 8: 725 # Add sizeof(uint32_t) to data_ptr to get the flags pointer 726 flags_ptr = kern.GetValueFromAddress(unsigned(data_ptr) + sizeof("uint32_t")) 727 flags = unsigned(dereference(Cast(flags_ptr, 'uint32_t *'))) 728 out_str += " +----- [Interrupt Controller {ic}] vector {gsi}, {trigger_level}, {active}, {shareable}{messaged}\n" \ 729 .format(ic=matchingIC.name, gsi=hex(gsi), 730 trigger_level="level trigger" if flags & kInterruptTriggerModeLevel else "edge trigger", 731 active="active low" if flags & kInterruptPolarityLow else "active high", 732 shareable="shareable" if flags & kInterruptIsShareable else "exclusive", 733 messaged=", messaged" if flags & kIOInterruptTypePCIMessaged else "") 734 else: 735 out_str += " +----- [Interrupt Controller {ic}] vector {gsi}\n".format(ic=matchingIC.name, gsi=hex(gsi)) 736 if should_print: 737 print("[ {entry_name}{entry_location} ]{msi_mode}\n{out_str}" \ 738 .format(entry_name=entry_name, 739 entry_location=entry_location, 740 msi_mode=" - MSIs enabled" if msi_mode else "", 741 out_str=out_str)) 742 743@lldb_command("showiokitclasshierarchy") 744def ShowIOKitClassHierarchy(cmd_args=None): 745 """ 746 Show class hierarchy for a IOKit class 747 """ 748 if cmd_args is None or len(cmd_args) == 0: 749 raise ArgumentError("Usage: showiokitclasshierarchy <IOKit class name>") 750 751 class_name = cmd_args[0] 752 metaclasses = GetMetaClasses() 753 if class_name not in metaclasses: 754 print("Class {} does not exist".format(class_name)) 755 return 756 metaclass = metaclasses[class_name] 757 758 # loop over superclasses 759 hierarchy = [] 760 current_metaclass = metaclass 761 while current_metaclass is not None: 762 hierarchy.insert(0, current_metaclass) 763 current_metaclass = current_metaclass.superclass() 764 765 for (index, mc) in enumerate(hierarchy): 766 indent = (" " * index) + "+---" 767 print("{}[ {} ] {}".format(indent, str(mc.className()), str(mc.data()))) 768 769 770###################################### 771# Helper routines 772###################################### 773def ShowRegistryEntryRecurse(entry, prefix, printProps): 774 """ prints registry entry summary and recurses through all its children. 775 """ 776 # Setup 777 global plane 778 out_string = "" 779 plen = (len(prefix)//2) 780 registryTable = entry.fRegistryTable 781 propertyTable = entry.fPropertyTable 782 783 # Print entry details 784 print("{0:s}{1:s}".format(prefix, GetRegistryEntrySummary(entry))) 785 # Printing large property tables make it look like lldb is 'stuck' 786 if printProps: 787 print(GetRegDictionary(propertyTable, prefix + " | ")) 788 789 # Recurse 790 if plane is None: 791 childKey = kern.globals.gIOServicePlane.keys[1] 792 else: 793 childKey = plane.keys[1] 794 childArray = LookupKeyInOSDict(registryTable, childKey) 795 if childArray is not None: 796 idx = 0 797 ca = CastIOKitClass(childArray, 'OSArray *') 798 count = unsigned(ca.count) 799 array = ca.array 800 while idx < count: 801 if plen != 0 and plen != 1 and (plen & (plen - 1)) == 0: 802 ShowRegistryEntryRecurse(CastIOKitClass(array[idx], 'IORegistryEntry *'), prefix + "| ", printProps) 803 else: 804 ShowRegistryEntryRecurse(CastIOKitClass(array[idx], 'IORegistryEntry *'), prefix + " ", printProps) 805 idx += 1 806 807def FindRegistryEntryRecurse(entry, search_name, stopAfterFirst): 808 """ Checks if given registry entry's name matches the search_name we're looking for 809 If yes, it prints the entry's summary and then recurses through its children 810 If no, it does nothing and recurses through its children 811 """ 812 # Setup 813 global plane 814 registryTable = entry.fRegistryTable 815 propertyTable = entry.fPropertyTable 816 817 # Compare 818 name = None 819 name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) 820 if name is None: 821 name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) 822 if name is None: 823 name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) 824 825 if name is not None: 826 if str(CastIOKitClass(name, 'OSString *').string) == search_name: 827 print(GetRegistryEntrySummary(entry)) 828 if stopAfterFirst is True: 829 return True 830 elif (pwrMgt := CastIOKitClass(entry, 'IOService *').pwrMgt) and (name := pwrMgt.Name): 831 if str(name) == search_name: 832 print(GetRegistryEntrySummary(entry)) 833 if stopAfterFirst is True: 834 return True 835 836 # Recurse 837 if plane is None: 838 childKey = kern.globals.gIOServicePlane.keys[1] 839 else: 840 childKey = plane.keys[1] 841 childArray = LookupKeyInOSDict(registryTable, childKey) 842 if childArray is not None: 843 idx = 0 844 ca = CastIOKitClass(childArray, 'OSArray *') 845 array = ca.array 846 count = unsigned(ca.count) 847 while idx < count: 848 if FindRegistryEntryRecurse(CastIOKitClass(array[idx], 'IORegistryEntry *'), search_name, stopAfterFirst) is True: 849 return True 850 idx += 1 851 return False 852 853def FindRegistryObjectRecurse(entry, search_name): 854 """ Checks if given registry entry's name matches the search_name we're looking for 855 If yes, return the entry 856 If no, it does nothing and recurses through its children 857 Implicitly stops after finding the first entry 858 """ 859 # Setup 860 global plane 861 registryTable = entry.fRegistryTable 862 propertyTable = entry.fPropertyTable 863 864 # Compare 865 name = None 866 name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) 867 if name is None: 868 name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) 869 if name is None: 870 name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) 871 872 if name is not None: 873 if str(CastIOKitClass(name, 'OSString *').string) == search_name: 874 return entry 875 elif (pwrMgt := CastIOKitClass(entry, 'IOService *').pwrMgt) and (name := pwrMgt.Name): 876 if str(name) == search_name: 877 return entry 878 879 # Recurse 880 if plane is None: 881 childKey = kern.globals.gIOServicePlane.keys[1] 882 else: 883 childKey = plane.keys[1] 884 childArray = LookupKeyInOSDict(registryTable, childKey) 885 if childArray is not None: 886 ca = CastIOKitClass(childArray, 'OSArray *') 887 array = ca.array 888 for idx in range(ca.count): 889 registry_object = FindRegistryObjectRecurse(CastIOKitClass(array[idx], 'IORegistryEntry *'), search_name) 890 if not registry_object or int(registry_object) == int(0): 891 continue 892 else: 893 return registry_object 894 return None 895 896def ShowUserServiceRecursive(service, prefix, last, childServices, sortedServices): 897 # blatantly copied from GetRegistryEntrySummary 898 name = None 899 registryTable = service.fRegistryTable 900 propertyTable = service.fPropertyTable 901 name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) 902 if name is None: 903 name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) 904 if name is None: 905 name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) 906 name = GetString(CastIOKitClass(name, 'OSString *')) 907 sortedServices.append((service, f"{prefix}+-o {name}")) 908 if last: 909 prefix += " " 910 else: 911 prefix += "| " 912 if int(service) not in childServices: 913 return 914 children = childServices[int(service)] 915 if len(children) == 0: 916 return 917 childrenCount = len(children) 918 for idx in range(childrenCount): 919 ShowUserServiceRecursive(children[idx], prefix, idx == childrenCount - 1, childServices, sortedServices) 920 921def ShowUserServerSummary(server): 922 reasonStrings = { 923 1: "jetsam", 924 2: "signal", 925 3: "codesigning", 926 6: "dyld", 927 9: "exec", 928 23: "guard", 929 25: "sandbox", 930 26: "security", 931 28: "PAC exception", 932 30: "port space", 933 34: "Rosetta" 934 } 935 server = kern.GetValueFromAddress(server, "IOUserServer *") 936 services = server.fServices 937 serviceCount = services.count 938 services = services.array 939 print(f"IOUserServer {hex(server)} (task {hex(server.fOwningTask)}):") 940 if int(server.fTaskCrashReason) != 0: 941 reasonString = "Dext crash reason: " 942 if server.fTaskCrashReason.osr_namespace in reasonStrings: 943 reasonString += reasonStrings[server.fTaskCrashReason.osr_namespace] 944 if server.fTaskCrashReason.osr_namespace == 2: 945 reasonString += f", {server.fTaskCrashReason.osr_namespace.osr_ode}" 946 print(reasonString) 947 # Attempt to reconstruct registry hierarchy 948 childServices = {} 949 for serviceIdx in range(serviceCount): 950 service = CastIOKitClass(services[serviceIdx], "IOService *") 951 provider = service.__provider 952 if int(provider) not in childServices: 953 childServices[int(provider)] = [] 954 childServices[int(provider)].append(service) 955 rootServices = [] 956 for provider in childServices: 957 provider = kern.GetValueFromAddress(provider, "IOService *") 958 if int(provider.__provider) not in childServices: 959 rootServices.append(provider) 960 sortedServices = [] 961 for service in rootServices: 962 ShowUserServiceRecursive(service, "", True, childServices, sortedServices) 963 maxNameLen = -1 964 minNameLen = -1 965 for serviceData in sortedServices: 966 currNameLen = len(serviceData[1]) 967 if maxNameLen < 0 or currNameLen > maxNameLen: 968 maxNameLen = currNameLen 969 if minNameLen < 0 or currNameLen < minNameLen: 970 minNameLen = currNameLen 971 nameLen = maxNameLen + 4 972 print("wt: willTerminate") 973 print("dt: didTerminate") 974 print("sd: serverDied") 975 print("it: instantiated") 976 print("sr: started") 977 print("sp: stopped") 978 print("wp: willPower") 979 print("ps: powerState") 980 print("Service" + (nameLen - len("Service")) * " ", end = "") 981 print("Address wt dt sd it sr sp wp ps") 982 for serviceData in sortedServices: 983 service = serviceData[0] 984 currNameLen = len(serviceData[1]) 985 986 print(serviceData[1] + (nameLen - currNameLen) * " ", end = "") 987 print(f"{hex(serviceData[0])} ", end = "") 988 if int(service.reserved) == 0 or int(service.reserved.uvars) == 0: 989 print("") 990 continue 991 #print(f"service {hex(service)}") 992 wt = service.reserved.uvars.willTerminate 993 wt = "N " if int(wt) == 0 else "Y " 994 dt = service.reserved.uvars.didTerminate 995 dt = "N " if int(dt) == 0 else "Y " 996 sd = service.reserved.uvars.serverDied 997 sd = "N " if int(sd) == 0 else "Y " 998 it = service.reserved.uvars.instantiated 999 it = "N " if int(it) == 0 else "Y " 1000 sr = service.reserved.uvars.started 1001 sr = "N " if int(sr) == 0 else "Y " 1002 sp = service.reserved.uvars.stopped 1003 sp = "N " if int(sp) == 0 else "Y " 1004 wp = service.reserved.uvars.willPower 1005 wp = "N " if int(wp) == 0 else "Y " 1006 ps = service.reserved.uvars.powerState 1007 ps = "N " if int(ps) == 0 else "Y " 1008 print(wt + dt + sd + it + sr + sp + wp + ps) 1009 1010def CompareStringToOSSymbol(string, os_sym): 1011 """ 1012 Lexicographically compare python string to OSSymbol 1013 Params: 1014 string - python string 1015 os_sym - OSSymbol 1016 1017 Returns: 1018 0 if string == os_sym 1019 1 if string > os_sym 1020 -1 if string < os_sym 1021 """ 1022 os_sym_str = GetString(os_sym) 1023 if string > os_sym_str: 1024 return 1 1025 elif string < os_sym_str: 1026 return -1 1027 else: 1028 return 0 1029 1030class IOKitMetaClass(object): 1031 """ 1032 A class that represents a IOKit metaclass. This is used to represent the 1033 IOKit inheritance hierarchy. 1034 """ 1035 1036 def __init__(self, meta): 1037 """ 1038 Initialize a IOKitMetaClass object. 1039 1040 Args: 1041 meta (core.cvalue.value): A LLDB value representing a 1042 OSMetaClass *. 1043 """ 1044 self._meta = meta 1045 self._superclass = None 1046 1047 def data(self): 1048 return self._meta 1049 1050 def setSuperclass(self, superclass): 1051 """ 1052 Set the superclass for this metaclass. 1053 1054 Args: 1055 superclass (core.cvalue.value): A LLDB value representing a 1056 OSMetaClass *. 1057 """ 1058 self._superclass = superclass 1059 1060 def superclass(self): 1061 """ 1062 Get the superclass for this metaclass (set by the setSuperclass method). 1063 1064 Returns: 1065 core.cvalue.value: A LLDB value representing a OSMetaClass *. 1066 """ 1067 return self._superclass 1068 1069 def className(self): 1070 """ 1071 Get the name of the class this metaclass represents. 1072 1073 Returns: 1074 str: The class name 1075 """ 1076 return self._meta.className.string 1077 1078 def inheritsFrom(self, other): 1079 """ 1080 Check if the class represented by this metaclass inherits from a class 1081 represented by another metaclass. 1082 1083 Args: 1084 other (IOKitMetaClass): The other metaclass 1085 1086 Returns: 1087 bool: Returns True if this class inherits from the other class and 1088 False otherwise. 1089 """ 1090 current = self 1091 while current is not None: 1092 if current == other: 1093 return True 1094 else: 1095 current = current.superclass() 1096 1097 1098def GetRegistryEntryClassName(entry): 1099 """ 1100 Get the class name of a registry entry. 1101 1102 Args: 1103 entry (core.cvalue.value): A LLDB value representing a 1104 IORegistryEntry *. 1105 1106 Returns: 1107 str: The class name of the entry or None if a class name could not be 1108 found. 1109 """ 1110 # Check using IOClass key 1111 result = LookupKeyInOSDict(entry.fPropertyTable, kern.globals.gIOClassKey) 1112 if result is not None: 1113 return GetString(result).replace("\"", "") 1114 else: 1115 # Use the vtable of the entry to determine the concrete type 1116 vt = dereference(Cast(entry, 'uintptr_t *')) - 2 * sizeof('uintptr_t') 1117 vt = kern.StripKernelPAC(vt) 1118 vtype = kern.SymbolicateFromAddress(vt) 1119 if len(vtype) > 0: 1120 vtableName = vtype[0].GetName() 1121 return vtableName[11:] # strip off "vtable for " 1122 else: 1123 return None 1124 1125 1126def GetRegistryEntryName(entry): 1127 """ 1128 Get the name of a registry entry. 1129 1130 Args: 1131 entry (core.cvalue.value): A LLDB value representing a 1132 IORegistryEntry *. 1133 1134 Returns: 1135 str: The name of the entry or None if a name could not be found. 1136 """ 1137 name = None 1138 1139 # First check the IOService plane nameKey 1140 result = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIOServicePlane.nameKey) 1141 if result is not None: 1142 name = GetString(result) 1143 1144 # Check the global IOName key 1145 if name is None: 1146 result = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIONameKey) 1147 if result is not None: 1148 name = GetString(result) 1149 1150 # Check the IOClass key 1151 if name is None: 1152 result = LookupKeyInOSDict(entry.fPropertyTable, kern.globals.gIOClassKey) 1153 if result is not None: 1154 name = GetString(result) 1155 1156 # Remove extra quotes 1157 if name is not None: 1158 return name.replace("\"", "") 1159 else: 1160 return GetRegistryEntryClassName(entry) 1161 1162 1163def GetRegistryEntryLocationInPlane(entry, plane): 1164 """ 1165 Get the registry entry location in a IOKit plane. 1166 1167 Args: 1168 entry (core.cvalue.value): A LLDB value representing a 1169 IORegistryEntry *. 1170 plane: An IOKit plane such as kern.globals.gIOServicePlane. 1171 1172 Returns: 1173 str: The location of the entry or None if a location could not be 1174 found. 1175 """ 1176 # Check the plane's pathLocationKey 1177 sym = LookupKeyInOSDict(entry.fRegistryTable, plane.pathLocationKey) 1178 1179 # Check the global IOLocation key 1180 if sym is None: 1181 sym = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIOLocationKey) 1182 if sym is not None: 1183 return GetString(sym).replace("\"", "") 1184 else: 1185 return None 1186 1187 1188@caching.cache_dynamically 1189def GetMetaClasses(target=None): 1190 """ 1191 Enumerate all IOKit metaclasses. Uses dynamic caching. 1192 1193 Returns: 1194 Dict[str, IOKitMetaClass]: A dictionary mapping each metaclass name to 1195 a IOKitMetaClass object representing the metaclass. 1196 """ 1197 1198 # This method takes a while, so it prints a progress indicator 1199 print("Enumerating IOKit metaclasses: ") 1200 1201 do_progress = os.isatty(sys.__stderr__.fileno()) 1202 1203 # Iterate over all classes present in sAllClassesDict 1204 count = unsigned(kern.globals.sAllClassesDict.count) 1205 metaclasses_by_address = {} 1206 for idx in range(count): 1207 if do_progress and idx % 10 == 0: 1208 sys.stderr.write("\033[K {} metaclass found...\r".format(idx)) 1209 1210 # Address of metaclass 1211 address = kern.globals.sAllClassesDict.dictionary[idx].value 1212 1213 # Create IOKitMetaClass and store in dict 1214 metaclasses_by_address[int(address)] = IOKitMetaClass(CastIOKitClass(kern.globals.sAllClassesDict.dictionary[idx].value, 'OSMetaClass *')) 1215 1216 # At this point, each metaclass is independent of each other. We don't have superclass links set up yet. 1217 1218 for address, metaclass in metaclasses_by_address.items(): 1219 # Get the address of the superclass using the superClassLink in IOMetaClass 1220 superclass_address = int(metaclass.data().superClassLink) 1221 1222 # Skip null superclass 1223 if superclass_address == 0: 1224 continue 1225 1226 # Find the superclass object in the dict 1227 if superclass_address in metaclasses_by_address: 1228 metaclass.setSuperclass(metaclasses_by_address[superclass_address]) 1229 else: 1230 print("warning: could not find superclass for {}".format(str(metaclass.data()))) 1231 1232 # This method returns a dictionary mapping each class name to the associated metaclass object 1233 metaclasses_by_name = {} 1234 for idx, (_, metaclass) in enumerate(metaclasses_by_address.items()): 1235 if do_progress and idx % 10 == 0: 1236 sys.stderr.write("\033[K {} metaclass indexed...\r".format(idx)) 1237 1238 metaclasses_by_name[str(metaclass.className())] = metaclass 1239 1240 print(" Indexed {} IOKit metaclasses.".format(count)) 1241 return metaclasses_by_name 1242 1243 1244def GetMatchingEntries(matcher): 1245 """ 1246 Iterate over the IOKit registry and find entries that match specific 1247 criteria. 1248 1249 Args: 1250 matcher (function): A matching function that returns True for a match 1251 and False otherwise. 1252 1253 Yields: 1254 core.cvalue.value: LLDB values that represent IORegistryEntry * for 1255 each registry entry found. 1256 """ 1257 1258 # Perform a BFS over the IOKit registry tree 1259 bfs_queue = deque() 1260 bfs_queue.append(kern.globals.gRegistryRoot) 1261 while len(bfs_queue) > 0: 1262 # Dequeue an entry 1263 entry = bfs_queue.popleft() 1264 1265 # Check if entry matches 1266 if matcher(entry): 1267 yield entry 1268 1269 # Find children of this entry and enqueue them 1270 child_array = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIOServicePlane.keys[1]) 1271 if child_array is not None: 1272 idx = 0 1273 ca = CastIOKitClass(child_array, 'OSArray *') 1274 count = unsigned(ca.count) 1275 while idx < count: 1276 bfs_queue.append(CastIOKitClass(ca.array[idx], 'IORegistryEntry *')) 1277 idx += 1 1278 1279 1280def FindMatchingServices(matching_name): 1281 """ 1282 Finds registry entries that match the given string. Works similarly to: 1283 1284 io_iterator_t iter; 1285 IOServiceGetMatchingServices(..., IOServiceMatching(matching_name), &iter); 1286 while (( io_object_t next = IOIteratorNext(iter))) { ... } 1287 1288 Args: 1289 matching_name (str): The class name to search for. 1290 1291 Yields: 1292 core.cvalue.value: LLDB values that represent IORegistryEntry * for 1293 each registry entry found. 1294 """ 1295 1296 # Check if the argument is valid 1297 metaclasses = GetMetaClasses() 1298 if matching_name not in metaclasses: 1299 return 1300 matching_metaclass = metaclasses[matching_name] 1301 1302 # An entry matches if it inherits from matching_metaclass 1303 def matcher(entry): 1304 # Get the class name of the entry and the associated metaclass 1305 entry_name = GetRegistryEntryClassName(entry) 1306 if entry_name in metaclasses: 1307 entry_metaclass = metaclasses[entry_name] 1308 return entry_metaclass.inheritsFrom(matching_metaclass) 1309 else: 1310 return False 1311 1312 # Search for entries 1313 for entry in GetMatchingEntries(matcher): 1314 yield entry 1315 1316 1317def GetRegistryEntryParent(entry, iokit_plane=None): 1318 """ 1319 Gets the parent entry of a registry entry. 1320 1321 Args: 1322 entry (core.cvalue.value): A LLDB value representing a 1323 IORegistryEntry *. 1324 iokit_plane (core.cvalue.value, optional): A LLDB value representing a 1325 IORegistryPlane *. By default, this method uses the IOService 1326 plane. 1327 1328 Returns: 1329 core.cvalue.value: A LLDB value representing a IORegistryEntry* that 1330 is the parent entry of the entry argument in the specified plane. 1331 Returns None if no entry could be found. 1332 """ 1333 kParentSetIndex = 0 1334 parent_key = None 1335 if iokit_plane is None: 1336 parent_key = kern.globals.gIOServicePlane.keys[kParentSetIndex] 1337 else: 1338 parent_key = plane.keys[kParentSetIndex] 1339 parent_array = LookupKeyInOSDict(entry.fRegistryTable, parent_key) 1340 parent_entry = None 1341 if parent_array is not None: 1342 idx = 0 1343 ca = CastIOKitClass(parent_array, 'OSArray *') 1344 count = unsigned(ca.count) 1345 if count > 0: 1346 parent_entry = CastIOKitClass(ca.array[0], 'IORegistryEntry *') 1347 return parent_entry 1348 1349 1350def GetRegistryEntryInterruptProperties(entry): 1351 """ 1352 Get the interrupt properties of a registry entry. 1353 1354 Args: 1355 entry (core.cvalue.value): A LLDB value representing a IORegistryEntry *. 1356 1357 Returns: 1358 (bool, List[core.cvalue.value], List[str]): A tuple with the following 1359 fields: 1360 - First field (bool): Whether this entry has a non-null 1361 IOPCIMSIMode. 1362 - Second field (List[core.cvalue.value]): A list of LLDB values 1363 representing OSData *. The OSData* pointer points to 1364 interrupt vector data. 1365 - Third field (List[str]): A list of strings representing the 1366 interrupt controller names from the 1367 IOInterruptControllers property. 1368 """ 1369 INTERRUPT_SPECIFIERS_PROPERTY = "IOInterruptSpecifiers" 1370 INTERRUPT_CONTROLLERS_PROPERTY = "IOInterruptControllers" 1371 MSI_MODE_PROPERTY = "IOPCIMSIMode" 1372 1373 # Check IOInterruptSpecifiers 1374 interrupt_specifiers = LookupKeyInPropTable(entry.fPropertyTable, INTERRUPT_SPECIFIERS_PROPERTY) 1375 if interrupt_specifiers is not None: 1376 interrupt_specifiers = CastIOKitClass(interrupt_specifiers, 'OSArray *') 1377 1378 # Check IOInterruptControllers 1379 interrupt_controllers = LookupKeyInPropTable(entry.fPropertyTable, INTERRUPT_CONTROLLERS_PROPERTY) 1380 if interrupt_controllers is not None: 1381 interrupt_controllers = CastIOKitClass(interrupt_controllers, 'OSArray *') 1382 1383 # Check MSI mode 1384 msi_mode = LookupKeyInPropTable(entry.fPropertyTable, MSI_MODE_PROPERTY) 1385 1386 result_vector_data = [] 1387 result_vector_cont = [] 1388 if interrupt_specifiers is not None and interrupt_controllers is not None: 1389 interrupt_specifiers_array_count = unsigned(interrupt_specifiers.count) 1390 interrupt_controllers_array_count = unsigned(interrupt_controllers.count) 1391 # The array lengths should be the same 1392 if interrupt_specifiers_array_count == interrupt_controllers_array_count and interrupt_specifiers_array_count > 0: 1393 idx = 0 1394 while idx < interrupt_specifiers_array_count: 1395 # IOInterruptSpecifiers is an array of OSData * 1396 vector_data = CastIOKitClass(interrupt_specifiers.array[idx], "OSData *") 1397 1398 # IOInterruptControllers is an array of OSString * 1399 vector_cont = GetString(interrupt_controllers.array[idx]) 1400 1401 result_vector_data.append(vector_data) 1402 result_vector_cont.append(vector_cont) 1403 idx += 1 1404 1405 return (msi_mode is not None, result_vector_data, result_vector_cont) 1406 1407 1408class InterruptControllerDevice(object): 1409 """Represents a IOInterruptController""" 1410 1411 def __init__(self, device, driver, base_vector_number, name): 1412 """ 1413 Initialize a InterruptControllerDevice. 1414 1415 Args: 1416 device (core.cvalue.value): The device object. 1417 driver (core.cvalue.value): The driver object. 1418 base_vector_number (int): The base interrupt vector. 1419 name (str): The name of this interrupt controller. 1420 1421 Note: 1422 Use the factory method makeInterruptControllerDevice to validate 1423 properties. 1424 """ 1425 self.device = device 1426 self.driver = driver 1427 self.name = name 1428 self.base_vector_number = base_vector_number 1429 1430 1431 def __str__(self): 1432 """ 1433 String representation of this InterruptControllerDevice. 1434 """ 1435 return " Name {}, base vector = {}, device = {}, driver = {}".format( 1436 self.name, hex(self.base_vector_number), str(self.device), str(self.driver)) 1437 1438 @staticmethod 1439 def makeInterruptControllerDevice(device, driver): 1440 """ 1441 Factory method to create a InterruptControllerDevice. 1442 1443 Args: 1444 device (core.cvalue.value): The device object. 1445 driver (core.cvalue.value): The driver object. 1446 1447 Returns: 1448 InterruptControllerDevice: Returns an instance of 1449 InterruptControllerDevice or None if the arguments do not have 1450 the required properties. 1451 """ 1452 BASE_VECTOR_PROPERTY = "Base Vector Number" 1453 INTERRUPT_CONTROLLER_NAME_PROPERTY = "InterruptControllerName" 1454 base_vector = LookupKeyInPropTable(device.fPropertyTable, BASE_VECTOR_PROPERTY) 1455 if base_vector is None: 1456 base_vector = LookupKeyInPropTable(driver.fPropertyTable, BASE_VECTOR_PROPERTY) 1457 device_name = LookupKeyInPropTable(device.fPropertyTable, INTERRUPT_CONTROLLER_NAME_PROPERTY) 1458 if device_name is None: 1459 device_name = LookupKeyInPropTable(driver.fPropertyTable, INTERRUPT_CONTROLLER_NAME_PROPERTY) 1460 1461 if device_name is not None: 1462 # Some interrupt controllers do not have a base vector number. Assume it is 0. 1463 base_vector_number = 0 1464 if base_vector is not None: 1465 base_vector_number = unsigned(GetNumber(base_vector)) 1466 device_name = GetString(device_name) 1467 # Construct object and return 1468 return InterruptControllerDevice(device, driver, base_vector_number, device_name) 1469 else: 1470 # error case 1471 return None 1472 1473 1474def SearchInterruptControllerDrivers(): 1475 """ 1476 Search the IOKit registry for entries that match IOInterruptController. 1477 1478 Yields: 1479 core.cvalue.value: A LLDB value representing a IORegistryEntry * that 1480 inherits from IOInterruptController. 1481 """ 1482 for entry in FindMatchingServices("IOInterruptController"): 1483 # Get parent 1484 parent = GetRegistryEntryParent(entry) 1485 1486 # Make the interrupt controller object 1487 ic = InterruptControllerDevice.makeInterruptControllerDevice(parent, entry) 1488 1489 # Yield object 1490 if ic is not None: 1491 yield ic 1492 1493 1494def LookupKeyInOSDict(osdict, key, comparer = None): 1495 """ Returns the value corresponding to a given key in a OSDictionary 1496 Returns None if the key was not found 1497 """ 1498 if not osdict: 1499 return 1500 count = unsigned(osdict.count) 1501 result = None 1502 idx = 0 1503 1504 dictionary = osdict.dictionary 1505 key_value = unsigned(key) if type(key) is value else key 1506 while idx < count and result is None: 1507 elem = dictionary[idx] 1508 if comparer is not None: 1509 if comparer(key, elem.key) == 0: 1510 result = elem.value 1511 elif key_value == unsigned(elem.key): 1512 result = elem.value 1513 idx += 1 1514 return result 1515 1516def LookupKeyInPropTable(propertyTable, key_str): 1517 """ Returns the value corresponding to a given key from a registry entry's property table 1518 Returns None if the key was not found 1519 The property that is being searched for is specified as a string in key_str 1520 """ 1521 if not propertyTable: 1522 return 1523 count = unsigned(propertyTable.count) 1524 result = None 1525 idx = 0 1526 while idx < count and result is None: 1527 if key_str == str(propertyTable.dictionary[idx].key.string): 1528 result = propertyTable.dictionary[idx].value 1529 idx += 1 1530 return result 1531 1532def GetRegDictionary(osdict, prefix): 1533 """ Returns a specially formatted string summary of the given OSDictionary 1534 This is done in order to pretty-print registry property tables in showregistry 1535 and other macros 1536 """ 1537 out_string = prefix + "{\n" 1538 idx = 0 1539 count = unsigned(osdict.count) 1540 1541 dictionary = osdict.dictionary 1542 while idx < count: 1543 entry = dictionary[idx] 1544 out_string += prefix + " " + GetObjectSummary(entry.key) + " = " + GetObjectSummary(entry.value) + "\n" 1545 idx += 1 1546 out_string += prefix + "}\n" 1547 return out_string 1548 1549def GetString(string): 1550 """ Returns the python string representation of a given OSString 1551 """ 1552 out_string = "{0:s}".format(CastIOKitClass(string, 'OSString *').string) 1553 return out_string 1554 1555def GetNumber(num): 1556 out_string = "{0:d}".format(CastIOKitClass(num, 'OSNumber *').value) 1557 return out_string 1558 1559def GetBoolean(b): 1560 """ Shows info about a given OSBoolean 1561 """ 1562 out_string = "" 1563 if b == kern.globals.gOSBooleanFalse: 1564 out_string += "No" 1565 else: 1566 out_string += "Yes" 1567 return out_string 1568 1569def GetMetaClass(mc): 1570 """ Shows info about a given OSSymbol 1571 """ 1572 out_string = "{0: <5d}x {1: >5d} bytes {2:s}\n".format(mc.instanceCount, mc.classSize, mc.className.string) 1573 return out_string 1574 1575def GetArray(arr): 1576 """ Returns a string containing info about a given OSArray 1577 """ 1578 out_string = "" 1579 idx = 0 1580 count = unsigned(arr.count) 1581 1582 array = arr.array 1583 while idx < count: 1584 obj = array[idx] 1585 idx += 1 1586 out_string += GetObjectSummary(obj) 1587 if idx < count: 1588 out_string += "," 1589 return out_string 1590 1591def GetDictionary(d): 1592 """ Returns a string containing info about a given OSDictionary 1593 """ 1594 if d is None: 1595 return "" 1596 out_string = "{\n" 1597 idx = 0 1598 count = unsigned(d.count) 1599 dictionary = d.dictionary 1600 while idx < count: 1601 entry = dictionary[idx] 1602 key = entry.key 1603 value = entry.value 1604 out_string += " \"{}\" = {}\n".format(GetString(key), GetObjectSummary(value)) 1605 idx += 1 1606 out_string += "}" 1607 return out_string 1608 1609def GetSet(se): 1610 """ Returns a string containing info about a given OSSet 1611 """ 1612 out_string = "[" + GetArray(se.members) + "]" 1613 return out_string 1614 1615def ReadIOPortInt(addr, numbytes, lcpu): 1616 """ Prints results after reading a given ioport 1617 """ 1618 result = 0xBAD10AD 1619 1620 if "kdp" != GetConnectionProtocol(): 1621 print("Target is not connected over kdp. Nothing to do here.") 1622 return 1623 1624 # Set up the manual KDP packet 1625 input_address = unsigned(addressof(kern.globals.manual_pkt.input)) 1626 len_address = unsigned(addressof(kern.globals.manual_pkt.len)) 1627 data_address = unsigned(addressof(kern.globals.manual_pkt.data)) 1628 if not WriteInt32ToMemoryAddress(0, input_address): 1629 print("0x{0: <4x}: 0x{1: <1x}".format(addr, result)) 1630 return 1631 1632 kdp_pkt_size = GetType('kdp_readioport_req_t').GetByteSize() 1633 if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): 1634 print("0x{0: <4x}: 0x{1: <1x}".format(addr, result)) 1635 return 1636 1637 kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_readioport_req_t *') 1638 1639 header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_READIOPORT'), length = kdp_pkt_size) 1640 1641 if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and 1642 WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and 1643 WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and 1644 WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))) and 1645 WriteInt32ToMemoryAddress(1, input_address) 1646 ): 1647 1648 result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_readioport_reply_t *') 1649 1650 if(result_pkt.error == 0): 1651 if numbytes == 1: 1652 result = dereference(Cast(addressof(result_pkt.data), 'uint8_t *')) 1653 elif numbytes == 2: 1654 result = dereference(Cast(addressof(result_pkt.data), 'uint16_t *')) 1655 elif numbytes == 4: 1656 result = dereference(Cast(addressof(result_pkt.data), 'uint32_t *')) 1657 1658 print("{0: <#6x}: {1:#0{2}x}".format(addr, result, (numbytes*2)+2)) 1659 1660def WriteIOPortInt(addr, numbytes, value, lcpu): 1661 """ Writes 'value' into ioport specified by 'addr'. Prints errors if it encounters any 1662 """ 1663 if "kdp" != GetConnectionProtocol(): 1664 print("Target is not connected over kdp. Nothing to do here.") 1665 return 1666 1667 # Set up the manual KDP packet 1668 input_address = unsigned(addressof(kern.globals.manual_pkt.input)) 1669 len_address = unsigned(addressof(kern.globals.manual_pkt.len)) 1670 data_address = unsigned(addressof(kern.globals.manual_pkt.data)) 1671 if not WriteInt32ToMemoryAddress(0, input_address): 1672 print("error writing {0: #x} to port {1: <#6x}: failed to write 0 to input_address".format(value, addr)) 1673 return 1674 1675 kdp_pkt_size = GetType('kdp_writeioport_req_t').GetByteSize() 1676 if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): 1677 print("error writing {0: #x} to port {1: <#6x}: failed to write kdp_pkt_size".format(value, addr)) 1678 return 1679 1680 kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_writeioport_req_t *') 1681 1682 header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_WRITEIOPORT'), length = kdp_pkt_size) 1683 1684 if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and 1685 WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and 1686 WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and 1687 WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))) 1688 ): 1689 if numbytes == 1: 1690 if not WriteInt8ToMemoryAddress(value, int(addressof(kgm_pkt.data))): 1691 print("error writing {0: #x} to port {1: <#6x}: failed to write 8 bit data".format(value, addr)) 1692 return 1693 elif numbytes == 2: 1694 if not WriteInt16ToMemoryAddress(value, int(addressof(kgm_pkt.data))): 1695 print("error writing {0: #x} to port {1: <#6x}: failed to write 16 bit data".format(value, addr)) 1696 return 1697 elif numbytes == 4: 1698 if not WriteInt32ToMemoryAddress(value, int(addressof(kgm_pkt.data))): 1699 print("error writing {0: #x} to port {1: <#6x}: failed to write 32 bit data".format(value, addr)) 1700 return 1701 if not WriteInt32ToMemoryAddress(1, input_address): 1702 print("error writing {0: #x} to port {1: <#6x}: failed to write to input_address".format(value, addr)) 1703 return 1704 1705 result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_writeioport_reply_t *') 1706 1707 # Done with the write 1708 if(result_pkt.error == 0): 1709 print("Writing {0: #x} to port {1: <#6x} was successful".format(value, addr)) 1710 else: 1711 print("error writing {0: #x} to port {1: <#6x}".format(value, addr)) 1712 1713@lldb_command('showinterruptcounts') 1714def showinterruptcounts(cmd_args=None): 1715 """ Shows event source based interrupt counts by nub name and interrupt index. 1716 Does not cover interrupts that are not event source based. Will report 0 1717 if interrupt accounting is disabled. 1718 """ 1719 1720 header_format = "{0: <20s} {1: >5s} {2: >20s}" 1721 content_format = "{0: <20s} {1: >5d} {2: >20d}" 1722 1723 print(header_format.format("Name", "Index", "Count")) 1724 1725 for i in kern.interrupt_stats: 1726 owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *') 1727 nub = CastIOKitClass(owner.provider, 'IORegistryEntry *') 1728 name = None 1729 1730 # To uniquely identify an interrupt, we need the nub name and the index. The index 1731 # is stored with the stats object, but we need to retrieve the name. 1732 1733 registryTable = nub.fRegistryTable 1734 propertyTable = nub.fPropertyTable 1735 1736 name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) 1737 if name is None: 1738 name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) 1739 if name is None: 1740 name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) 1741 1742 if name is None: 1743 nub_name = "Unknown" 1744 else: 1745 nub_name = GetString(CastIOKitClass(name, 'OSString *')) 1746 1747 # We now have everything we need; spew the requested data. 1748 1749 interrupt_index = i.interruptIndex 1750 first_level_count = i.interruptStatistics[0] 1751 1752 print(content_format.format(nub_name, interrupt_index, first_level_count)) 1753 1754 return True 1755 1756@lldb_command('showinterruptstats') 1757def showinterruptstats(cmd_args=None): 1758 """ Shows event source based interrupt statistics by nub name and interrupt index. 1759 Does not cover interrupts that are not event source based. Will report 0 1760 if interrupt accounting is disabled, or if specific statistics are disabled. 1761 Time is reported in ticks of mach_absolute_time. Statistics are: 1762 1763 Interrupt Count: Number of times the interrupt context handler was run 1764 Interrupt Time: Total time spent in the interrupt context handler (if any) 1765 Workloop Count: Number of times the kernel context handler was run 1766 Workloop CPU Time: Total CPU time spent running the kernel context handler 1767 Workloop Time: Total time spent running the kernel context handler 1768 """ 1769 1770 header_format = "{0: <20s} {1: >5s} {2: >20s} {3: >20s} {4: >20s} {5: >20s} {6: >20s} {7: >20s} {8: >20s} {9: >20s}" 1771 content_format = "{0: <20s} {1: >5d} {2: >20d} {3: >20d} {4: >20d} {5: >20d} {6: >20d} {7: >20d} {8: >20d} {9: >#20x}" 1772 1773 print(header_format.format("Name", "Index", "Interrupt Count", "Interrupt Time", "Avg Interrupt Time", "Workloop Count", "Workloop CPU Time", "Workloop Time", "Avg Workloop Time", "Owner")) 1774 1775 for i in kern.interrupt_stats: 1776 owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *') 1777 nub = CastIOKitClass(owner.provider, 'IORegistryEntry *') 1778 name = None 1779 1780 # To uniquely identify an interrupt, we need the nub name and the index. The index 1781 # is stored with the stats object, but we need to retrieve the name. 1782 1783 registryTable = nub.fRegistryTable 1784 propertyTable = nub.fPropertyTable 1785 1786 name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) 1787 if name is None: 1788 name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) 1789 if name is None: 1790 name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) 1791 1792 if name is None: 1793 nub_name = "Unknown" 1794 else: 1795 nub_name = GetString(CastIOKitClass(name, 'OSString *')) 1796 1797 # We now have everything we need; spew the requested data. 1798 1799 interrupt_index = i.interruptIndex 1800 first_level_count = i.interruptStatistics[0] 1801 second_level_count = i.interruptStatistics[1] 1802 first_level_time = i.interruptStatistics[2] 1803 second_level_cpu_time = i.interruptStatistics[3] 1804 second_level_system_time = i.interruptStatistics[4] 1805 1806 avg_first_level_time = 0 1807 if first_level_count != 0: 1808 avg_first_level_time = first_level_time // first_level_count 1809 1810 avg_second_level_time = 0 1811 if second_level_count != 0: 1812 avg_second_level_time = second_level_system_time // second_level_count 1813 1814 print(content_format.format(nub_name, interrupt_index, first_level_count, first_level_time, avg_first_level_time, 1815 second_level_count, second_level_cpu_time, second_level_system_time, avg_second_level_time, owner)) 1816 1817 return True 1818 1819def GetRegistryPlane(plane_name): 1820 """ 1821 Given plane_name, returns IORegistryPlane * object or None if there's no such registry plane 1822 """ 1823 return LookupKeyInOSDict(kern.globals.gIORegistryPlanes, plane_name, CompareStringToOSSymbol) 1824 1825def DecodePreoslogSource(source): 1826 """ 1827 Given preoslog source, return a matching string representation 1828 """ 1829 source_to_str = {0 : "iboot"} 1830 if source in source_to_str: 1831 return source_to_str[source] 1832 return "UNKNOWN" 1833 1834def GetPreoslogHeader(): 1835 """ 1836 Scan IODeviceTree for preoslog and return a python representation of it 1837 """ 1838 edt_plane = GetRegistryPlane("IODeviceTree") 1839 if edt_plane is None: 1840 print("Couldn't obtain a pointer to IODeviceTree") 1841 return None 1842 1843 # Registry API functions operate on "plane" global variable 1844 global plane 1845 prev_plane = plane 1846 plane = edt_plane 1847 chosen = FindRegistryObjectRecurse(kern.globals.gRegistryRoot, "chosen") 1848 if chosen is None: 1849 print("Couldn't obtain /chosen IORegistryEntry") 1850 return None 1851 1852 memory_map = FindRegistryObjectRecurse(chosen, "memory-map") 1853 if memory_map is None: 1854 print("Couldn't obtain memory-map from /chosen") 1855 return None 1856 1857 plane = prev_plane 1858 1859 mm_preoslog = LookupKeyInOSDict(memory_map.fPropertyTable, "preoslog", CompareStringToOSSymbol) 1860 if mm_preoslog is None: 1861 print("Couldn't find preoslog entry in memory-map") 1862 return None 1863 1864 if mm_preoslog.length != 16: 1865 print("preoslog entry in memory-map is malformed, expected len is 16, given len is {:d}".format(mm_preoslog.length)) 1866 return None 1867 1868 data = cast(mm_preoslog.data, "dtptr_t *") 1869 preoslog_paddr = unsigned(data[0]) 1870 preoslog_vaddr = kern.PhysToKernelVirt(preoslog_paddr) 1871 preoslog_size = unsigned(data[1]) 1872 1873 preoslog_header = PreoslogHeader() 1874 1875 # This structure defnition doesn't exist in xnu 1876 """ 1877 typedef struct __attribute__((packed)) { 1878 char magic[4]; 1879 uint32_t size; 1880 uint32_t offset; 1881 uint8_t source; 1882 uint8_t wrapped; 1883 char data[]; 1884 } preoslog_header_t; 1885 """ 1886 preoslog_header_ptr = kern.GetValueFromAddress(preoslog_vaddr, "uint8_t *") 1887 preoslog_header.magic = preoslog_header_ptr[0:4] 1888 preoslog_header.source = DecodePreoslogSource(unsigned(preoslog_header_ptr[12])) 1889 preoslog_header.wrapped = unsigned(preoslog_header_ptr[13]) 1890 preoslog_header_ptr = kern.GetValueFromAddress(preoslog_vaddr, "uint32_t *") 1891 preoslog_header.size = unsigned(preoslog_header_ptr[1]) 1892 preoslog_header.offset = unsigned(preoslog_header_ptr[2]) 1893 1894 for i in range(len(preoslog_header.valid_magic)): 1895 c = chr(unsigned(preoslog_header.magic[i])) 1896 if c != preoslog_header.valid_magic[i]: 1897 string = "Error: magic doesn't match, expected {:.4s}, given {:.4s}" 1898 print(string.format(preoslog_header.valid_magic, preoslog_header.magic)) 1899 return None 1900 1901 if preoslog_header.size != preoslog_size: 1902 string = "Error: size mismatch preoslog_header.size ({}) != preoslog_size ({})" 1903 print(string.format(preoslog_header.size, preoslog_size)) 1904 return None 1905 1906 preoslog_data_ptr = kern.GetValueFromAddress(preoslog_vaddr + 14, "char *") 1907 preoslog_header.data = preoslog_data_ptr.GetSBValue().GetPointeeData(0, preoslog_size) 1908 return preoslog_header 1909 1910@lldb_command("showpreoslog") 1911def showpreoslog(cmd_args=None): 1912 """ Display preoslog buffer """ 1913 1914 preoslog = GetPreoslogHeader() 1915 if preoslog is None: 1916 print("Error: couldn't obtain preoslog header") 1917 return False 1918 1919 header = "".join([ 1920 "----preoslog log header-----\n", 1921 "size - {} bytes\n", 1922 "write offset - {:#x}\n", 1923 "wrapped - {}\n", 1924 "source - {}\n", 1925 "----preoslog log start------" 1926 ]) 1927 1928 print(header.format(preoslog.size, preoslog.offset, preoslog.wrapped, preoslog.source)) 1929 1930 err = lldb.SBError() 1931 if preoslog.wrapped > 0: 1932 print(preoslog.data.GetString(err, preoslog.offset + 1)) 1933 1934 print(preoslog.data.GetString(err, 0).encode(errors='backslashreplace').decode()) 1935 print("-----preoslog log end-------") 1936 1937 if not err.success: 1938 raise RuntimeError(f"SBError when retreiving preoslog data: {err.GetDescription()}") 1939 1940 return True 1941 1942@lldb_command('showeventsources') 1943def ShowEventSources(cmd_args=None): 1944 """ Show all event sources for a IOWorkLoop 1945 syntax: (lldb) showeventsources <IOWorkLoop *> 1946 """ 1947 if cmd_args is None or len(cmd_args) == 0: 1948 raise ArgumentError("Please specify the address of the IOWorkLoop") 1949 1950 obj = kern.GetValueFromAddress(cmd_args[0], 'IOWorkLoop *') 1951 idx = 0 1952 event = obj.eventChain 1953 while event != 0: 1954 enabled = event.enabled 1955 print("{}: {} [{}]".format(idx, GetObjectSummary(event), "enabled" if enabled else "disabled")) 1956 event = event.eventChainNext 1957 idx += 1 1958 1959def GetRegionProp(propertyTable, pattern): 1960 """ Returns the list corresponding to a given pattern from a registry entry's property table 1961 Returns empty list if the key is not found 1962 The property that is being searched for is specified as a string in pattern 1963 """ 1964 if not propertyTable: 1965 return None 1966 1967 count = unsigned(propertyTable.count) 1968 result = [] 1969 res = None 1970 idx = 0 1971 while idx < count: 1972 res = re.search(pattern, str(propertyTable.dictionary[idx].key.string)) 1973 if res: 1974 result.append(res.group()) 1975 idx += 1 1976 1977 return result 1978 1979@lldb_command("showcarveouts") 1980def ShowCarveouts(cmd_args=None): 1981 """ 1982 Scan IODeviceTree for every object in carveout-memory-map and print the memory carveouts. 1983 syntax: (lldb) showcarveouts 1984 """ 1985 edt_plane = GetRegistryPlane("IODeviceTree") 1986 if edt_plane is None: 1987 print("Couldn't obtain a pointer to IODeviceTree") 1988 return None 1989 1990 # Registry API functions operate on "plane" global variable 1991 global plane 1992 prev_plane = plane 1993 plane = edt_plane 1994 1995 chosen = FindRegistryObjectRecurse(kern.globals.gRegistryRoot, "chosen") 1996 if chosen is None: 1997 print("Couldn't obtain /chosen IORegistryEntry") 1998 return None 1999 2000 memory_map = FindRegistryObjectRecurse(chosen, "carveout-memory-map") 2001 if memory_map is None: 2002 print("Couldn't obtain memory-map from /chosen/carveout-memory-map") 2003 return None 2004 2005 plane = prev_plane 2006 2007 """ 2008 Dynamically populated by iBoot to store memory region description 2009 region-id-<n>: <region n base> <region n size> 2010 region-name-id-<n>: <region n name> 2011 """ 2012 name_prop_list = [] 2013 range_prop_list = [] 2014 region_id_list = [] 2015 region_name_id_list = [] 2016 2017 region_id = re.compile(r"region-id-\d+") 2018 region_id_list = GetRegionProp(memory_map.fPropertyTable, region_id); 2019 region_name_id = re.compile(r"region-name-id-\d+") 2020 region_name_id_list = GetRegionProp(memory_map.fPropertyTable, region_name_id); 2021 2022 for names in region_name_id_list: 2023 mm_entry = LookupKeyInOSDict(memory_map.fPropertyTable, names, CompareStringToOSSymbol) 2024 if mm_entry is None: 2025 print("Couldn't find " + names + " entry in carveout-memory-map", file=sys.stderr) 2026 continue 2027 data = cast(mm_entry.data, "char *") 2028 string = "{:<32s}: " 2029 name_prop_list.append( string.format(data) ); 2030 2031 for ids in region_id_list: 2032 mm_entry = LookupKeyInOSDict(memory_map.fPropertyTable, ids, CompareStringToOSSymbol) 2033 if mm_entry is None: 2034 print("Couldn't find " + ids + " entry in carveout-memory-map") 2035 continue 2036 2037 data = cast(mm_entry.data, "dtptr_t *") 2038 paddr = unsigned(data[0]) 2039 size = unsigned(data[1]) 2040 2041 string = "0x{:x}-0x{:x} (size: 0x{:x})" 2042 range_prop_list.append(string.format(paddr, paddr+size, size)); 2043 2044 for namep, rangep in zip(name_prop_list, range_prop_list): 2045 print(namep, rangep) 2046 2047 return True 2048