1from collections import namedtuple 2from functools import cached_property 3import os 4import io 5from typing import Any, Generator 6import core 7from uuid import UUID 8 9from core.cvalue import ( 10 unsigned, 11 signed, 12 addressof 13) 14from core.caching import ( 15 cache_dynamically, 16 LazyTarget, 17) 18from core.io import SBProcessRawIO 19from macho import MachOSegment, MemMachO, VisualMachoMap 20 21from xnu import ( 22 IterateLinkedList, 23 lldb_alias, 24 lldb_command, 25 lldb_run_command, 26 lldb_type_summary, 27 kern, 28 Cast, 29 header, 30 GetLongestMatchOption, 31 debuglog, 32 dsymForUUID, 33 addDSYM, 34 loadDSYM, 35 ArgumentError, 36 ArgumentStringToInt, 37 GetObjectAtIndexFromArray, 38 ResolveFSPath, 39 uuid_regex, 40 GetLLDBThreadForKernelThread 41) 42 43import kmemory 44import macho 45import lldb 46import concurrent.futures 47 48 49# 50# Summary of information available about a kext. 51# 52# uuid - UUID of the object 53# vmaddr - VA of the text segment 54# name - Name of the kext 55# address - Kext address 56# segments - Mach-O segments (if available) 57# summary - OSKextLoadedSummary 58# kmod - kmod_info_t 59class KextSummary: 60 def __init__(self, uuid: str, vmaddr, name: str, address: int, segments: list[MachOSegment], summary: core.value): 61 self.uuid = uuid 62 self.vmaddr = vmaddr 63 self.name = name 64 self.address = address 65 self.segments = segments 66 self.summary = summary 67 68 @cached_property 69 def kmod(self): 70 try: 71 kmod = GetKmodWithAddr(unsigned(self.address)) 72 except ValueError: 73 kmod = None 74 75 return kmod 76 77# Segment helpers 78 79 80def text_segment(segments): 81 """ Return TEXT segment if present in the list of first one. 82 segments: List of MachOSegment. 83 """ 84 85 text_segments = { 86 s.name: s 87 for s in segments 88 if s.name in ('__TEXT_EXEC', '__TEXT') 89 } 90 91 # Pick text segment based on our prefered order. 92 for name in ['__TEXT_EXEC', '__TEXT']: 93 if name in text_segments: 94 return text_segments[name] 95 96 return segments[0] 97 98 99def seg_contains(segments, addr): 100 """ Returns generator of all segments that contains given address. """ 101 102 return ( 103 s for s in segments 104 if s.vmaddr <= addr < (s.vmaddr + s.vmsize) 105 ) 106 107 108def sec_contains(sections, addr): 109 """ Returns generator of all sections that contains given address. """ 110 111 return ( 112 s for s in sections 113 if s.addr <= addr < (s.addr + s.size) 114 ) 115 116def sbsec_contains(target, sbsections, addr): 117 """ Returns generator of all SBSections that contains given address. """ 118 119 return ( 120 s for s in sbsections 121 if s.GetLoadAddress(target) <= addr < s.GetLoadAddress(target) + s.GetByteSize() 122 ) 123 124 125# Summary helpers 126 127def LoadMachO(address, size): 128 """ Parses Mach-O headers in given VA range. 129 130 return: MemMachO instance. 131 """ 132 133 process = LazyTarget.GetProcess() 134 procio = SBProcessRawIO(process, address, size) 135 bufio = io.BufferedRandom(procio) 136 return macho.MemMachO(bufio) 137 138 139def IterateKextSummaries(target) -> Generator[KextSummary, Any, None]: 140 """ Generator walking over all kext summaries. """ 141 142 hdr = target.chkFindFirstGlobalVariable('gLoadedKextSummaries').Dereference() 143 arr = hdr.GetValueForExpressionPath('.summaries[0]') 144 total = hdr.xGetIntegerByName('numSummaries') 145 146 for kext in (core.value(e.AddressOf()) for e in arr.xIterSiblings(0, total)): 147 # Load Mach-O segments/sections. 148 mobj = LoadMachO(unsigned(kext.address), unsigned(kext.size)) 149 150 # Construct kext summary. 151 yield KextSummary( 152 uuid=GetUUIDSummary(kext.uuid), 153 vmaddr=text_segment(mobj.segments).vmaddr, 154 name=str(kext.name), 155 address=unsigned(kext.address), 156 segments=mobj.segments, 157 summary=kext 158 ) 159 160 161@cache_dynamically 162def GetAllKextSummaries(target=None) -> list[KextSummary]: 163 """ Return all kext summaries. (cached) """ 164 165 return list(IterateKextSummaries(target)) 166 167 168def FindKextSummary(kmod_addr): 169 """ Returns summary for given kmod_info_t. """ 170 171 for mod in GetAllKextSummaries(): 172 if mod.address == kmod_addr or mod.vmaddr == kmod_addr: 173 return mod 174 175 return None 176 177 178# Keep this around until DiskImages2 migrate over to new methods above. 179def GetKextLoadInformation(addr=0, show_progress=False): 180 """ Original wrapper kept for backwards compatibility. """ 181 if addr: 182 return [FindKextSummary(addr)] 183 else: 184 return GetAllKextSummaries() 185 186 187@lldb_command('showkextmacho') 188def ShowKextMachO(cmd_args=[]): 189 """ Show visual Mach-O layout. 190 191 Syntax: (lldb) showkextmacho <name of a kext> 192 """ 193 if len(cmd_args) != 1: 194 raise ArgumentError("kext name is missing") 195 196 for kext in GetAllKextSummaries(): 197 198 # Skip not matching kexts. 199 if kext.name.find(cmd_args[0]) == -1: 200 continue 201 202 # Load Mach-O segments/sections. 203 mobj = LoadMachO(unsigned(kext.kmod.address), unsigned(kext.kmod.size)) 204 205 p = VisualMachoMap(kext.name) 206 p.printMachoMap(mobj) 207 print(" \n") 208 209 210_UNKNOWN_UUID = "........-....-....-....-............" 211 212 213@lldb_type_summary(['uuid_t']) 214@header("") 215def GetUUIDSummary(uuid): 216 """ returns a UUID string in form CA50DA4C-CA10-3246-B8DC-93542489AA26 217 218 uuid - Address of a memory where UUID is stored. 219 """ 220 221 err = lldb.SBError() 222 addr = unsigned(addressof(uuid)) 223 data = LazyTarget.GetProcess().ReadMemory(addr, 16, err) 224 225 if not err.Success(): 226 return _UNKNOWN_UUID 227 228 return str(UUID(bytes=data)).upper() 229 230 231@lldb_type_summary(['kmod_info_t *']) 232@header(( 233 "{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: <20s} {6: <20s} " 234 "{7: >20s} {8: <30s}" 235).format('kmod_info', 'address', 'size', 'id', 'refs', 'TEXT exec', 'size', 236 'version', 'name')) 237def GetKextSummary(kmod): 238 """ returns a string representation of kext information """ 239 if not kmod: 240 return "kmod info missing" 241 242 format_string = ( 243 "{mod: <#020x} {mod.address: <#020x} {mod.size: <#020x} " 244 "{mod.id: >3d} {mod.reference_count: >5d} {seg.vmaddr: <#020x} " 245 "{seg.vmsize: <#020x} {mod.version: >20s} {mod.name: <30s}" 246 ) 247 248 # Try to obtain text segment from kext summary 249 summary = FindKextSummary(unsigned(kmod.address)) 250 if summary: 251 seg = text_segment(summary.segments) 252 else: 253 # Fake text segment for pseudo kexts. 254 seg = MachOSegment('__TEXT', kmod.address, kmod.size, 0, kmod.size, []) 255 256 return format_string.format(mod=kmod, seg=seg) 257 258 259def GetKmodWithAddr(addr): 260 """ Go through kmod list and find one with begin_addr as addr. 261 returns: None if not found else a cvalue of type kmod. 262 """ 263 264 for kmod in IterateLinkedList(kern.globals.kmod, 'next'): 265 if addr == unsigned(kmod.address): 266 return kmod 267 268 return None 269 270 271@lldb_command('showkmodaddr') 272def ShowKmodAddr(cmd_args=[]): 273 """ Given an address, print the offset and name for the kmod containing it 274 Syntax: (lldb) showkmodaddr <addr> 275 """ 276 if len(cmd_args) < 1: 277 raise ArgumentError("Insufficient arguments") 278 279 addr = ArgumentStringToInt(cmd_args[0]) 280 281 # Find first summary/segment pair that covers given address. 282 sumseg = ( 283 (m, next(seg_contains(m.segments, addr), None)) 284 for m in GetAllKextSummaries() 285 ) 286 287 print(GetKextSummary.header) 288 for ksum, segment in (t for t in sumseg if t[1] is not None): 289 summary = GetKextSummary(ksum.kmod) 290 print(summary + " segment: {} offset = {:#0x}".format( 291 segment.name, (addr - segment.vmaddr))) 292 293 return True 294 295 296def GetOSKextVersion(version_num): 297 """ returns a string of format 1.2.3x from the version_num 298 params: version_num - int 299 return: str 300 """ 301 if version_num == -1: 302 return "invalid" 303 304 (MAJ_MULT, MIN_MULT) = (1000000000000, 100000000) 305 (REV_MULT, STAGE_MULT) = (10000, 1000) 306 307 version = version_num 308 309 vers_major = version // MAJ_MULT 310 version = version - (vers_major * MAJ_MULT) 311 312 vers_minor = version // MIN_MULT 313 version = version - (vers_minor * MIN_MULT) 314 315 vers_revision = version // REV_MULT 316 version = version - (vers_revision * REV_MULT) 317 318 vers_stage = version // STAGE_MULT 319 version = version - (vers_stage * STAGE_MULT) 320 321 vers_stage_level = version 322 323 out_str = "%d.%d" % (vers_major, vers_minor) 324 if vers_revision > 0: 325 out_str += ".%d" % vers_revision 326 if vers_stage == 1: 327 out_str += "d%d" % vers_stage_level 328 if vers_stage == 3: 329 out_str += "a%d" % vers_stage_level 330 if vers_stage == 5: 331 out_str += "b%d" % vers_stage_level 332 if vers_stage == 6: 333 out_str += "fc%d" % vers_stage_level 334 335 return out_str 336 337 338def FindKmodNameForAddr(addr): 339 """ Given an address, return the name of the kext containing that address. 340 """ 341 342 names = ( 343 mod.kmod.name 344 for mod in GetAllKextSummaries() 345 if (any(seg_contains(mod.segments, unsigned(addr)))) 346 ) 347 348 return next(names, None) 349 350 351@lldb_command('showallkmods') 352def ShowAllKexts(cmd_args=None): 353 """ Display a summary listing of all loaded kexts (alias: showallkmods) """ 354 355 print("{: <36s} ".format("UUID") + GetKextSummary.header) 356 357 for kmod in IterateLinkedList(kern.globals.kmod, 'next'): 358 sum = FindKextSummary(unsigned(kmod.address)) 359 360 if sum: 361 _ksummary = GetKextSummary(sum.kmod) 362 uuid = sum.uuid 363 else: 364 _ksummary = GetKextSummary(kmod) 365 uuid = _UNKNOWN_UUID 366 367 print(uuid + " " + _ksummary) 368 369 370@lldb_command('showallknownkmods') 371def ShowAllKnownKexts(cmd_args=None): 372 """ Display a summary listing of all kexts known in the system. 373 This is particularly useful to find if some kext was unloaded 374 before this crash'ed state. 375 """ 376 kext_ptr = kern.globals.sKextsByID 377 kext_count = unsigned(kext_ptr.count) 378 379 print("%d kexts in sKextsByID:" % kext_count) 380 print("{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name')) 381 format_string = "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}" 382 383 for kext_dict in (GetObjectAtIndexFromArray(kext_ptr.dictionary, i) 384 for i in range(kext_count)): 385 386 kext_name = str(kext_dict.key.string) 387 osk = Cast(kext_dict.value, 'OSKext *') 388 389 load_addr = "------" 390 id = "--" 391 392 if int(osk.flags.loaded): 393 load_addr = "{0: <#020x}".format(osk.kmod_info) 394 id = "{0: >5d}".format(osk.loadTag) 395 396 version_num = signed(osk.version) 397 version = GetOSKextVersion(version_num) 398 print(format_string.format(osk, load_addr, id, version, kext_name)) 399 400 401def FetchDSYM(kinfo): 402 """ Obtains and adds dSYM based on kext summary. """ 403 404 # No op for built-in modules. 405 kernel_uuid = str(kern.globals.kernel_uuid_string) 406 if kernel_uuid == kinfo.uuid: 407 print("(built-in)") 408 return 409 410 # Obtain and load binary from dSYM. 411 print("Fetching dSYM for %s" % kinfo.uuid) 412 info = dsymForUUID(kinfo.uuid) 413 if info and 'DBGSymbolRichExecutable' in info: 414 print("Adding dSYM (%s) for %s" % (kinfo.uuid, info['DBGSymbolRichExecutable'])) 415 addDSYM(kinfo.uuid, info) 416 loadDSYM(kinfo.uuid, kinfo.vmaddr, kinfo.segments) 417 else: 418 print("Failed to get symbol info for %s" % kinfo.uuid) 419 420 421def AddKextSymsByFile(filename, slide): 422 """ Add kext based on file name and slide. """ 423 sections = None 424 425 filespec = lldb.SBFileSpec(filename, False) 426 print("target modules add \"{:s}\"".format(filename)) 427 print(lldb_run_command("target modules add \"{:s}\"".format(filename))) 428 429 loaded_module = LazyTarget.GetTarget().FindModule(filespec) 430 if loaded_module.IsValid(): 431 uuid_str = loaded_module.GetUUIDString() 432 debuglog("added module {:s} with uuid {:s}".format(filename, uuid_str)) 433 434 if slide is None: 435 for k in GetAllKextSummaries(): 436 debuglog(k.uuid) 437 if k.uuid.lower() == uuid_str.lower(): 438 slide = k.vmaddr 439 sections = k.segments 440 debuglog("found the slide {:#0x} for uuid {:s}".format(k.vmaddr, k.uuid)) 441 if slide is None: 442 raise ArgumentError("Unable to find load address for module described at {:s} ".format(filename)) 443 444 if not sections: 445 cmd_str = "target modules load --file \"{:s}\" --slide {:s}".format(filename, str(slide)) 446 debuglog(cmd_str) 447 else: 448 cmd_str = "target modules load --file \"{:s}\"".format(filename) 449 for s in sections: 450 cmd_str += " {:s} {:#0x} ".format(s.name, s.vmaddr) 451 debuglog(cmd_str) 452 453 lldb.debugger.HandleCommand(cmd_str) 454 455 kern.symbolicator = None 456 return True 457 458 459def AddKextSymsByName(kextname, all=False): 460 """ Add kext based on longest name match""" 461 462 kexts = GetLongestMatchOption(kextname, [x.name for x in GetAllKextSummaries()], True) 463 if not kexts: 464 print("No matching kext found.") 465 return False 466 467 if len(kexts) != 1 and not all: 468 print("Ambiguous match for name: {:s}".format(kextname)) 469 if len(kexts) > 0: 470 print("Options are:\n\t" + "\n\t".join(kexts)) 471 return False 472 473 # Load all matching dSYMs 474 for sum in GetAllKextSummaries(): 475 if sum.name in kexts: 476 debuglog("matched the kext to name {:s} " 477 "and uuid {:s}".format(sum.name, sum.uuid)) 478 FetchDSYM(sum) 479 480 kern.symbolicator = None 481 return True 482 483 484def AddKextByAddress(addr: str): 485 """ Given an address, load the kext which contains that address """ 486 487 match = ( 488 (kinfo, seg_contains(kinfo.segments, addr)) 489 for kinfo in GetAllKextSummaries() 490 if any(seg_contains(kinfo.segments, addr)) 491 ) 492 493 # Load all kexts which contain given address. 494 print(GetKextSummary.header) 495 for kinfo, segs in match: 496 for s in segs: 497 print(f"{GetKextSummary(kinfo.kmod)} segment: {s.name} offset = {(addr - s.vmaddr):0x}") 498 FetchDSYM(kinfo) 499 500 501def AddKextByThread(addr: str): 502 """ Given a thread, load all kexts needed to symbolicate its backtrace """ 503 504 thread_value = kern.GetValueFromAddress(addr, "thread_t") 505 thread_lldb_SBThread = GetLLDBThreadForKernelThread(thread_value) 506 507 kexts_needed = dict() 508 printed_header = False 509 for frame in thread_lldb_SBThread.frames: 510 if not frame.name: 511 frame_addr = frame.GetPC() 512 513 match = ( 514 (kinfo, seg_contains(kinfo.segments, frame_addr)) 515 for kinfo in GetAllKextSummaries() 516 if any(seg_contains(kinfo.segments, frame_addr)) 517 ) 518 519 if match and not printed_header: 520 print(GetKextSummary.header) 521 printed_header = True 522 523 for kinfo, segs in match: 524 for s in segs: 525 print(f"{GetKextSummary(kinfo.kmod)} segment: {s.name} offset = {(frame_addr - s.vmaddr):0x}") 526 kexts_needed[kinfo.uuid] = kinfo 527 528 print(f"Fetching {len(kexts_needed)} dSyms") 529 pool = concurrent.futures.ThreadPoolExecutor() 530 for kinfo in kexts_needed.values(): 531 pool.submit(FetchDSYM, kinfo) 532 pool.shutdown(wait=True) 533 534 535def AddKextByUUID(uuid: str): 536 """ Loads the dSym for a specific UUID, or all dSym """ 537 538 kernel_uuid = str(kern.globals.kernel_uuid_string).lower() 539 load_all_kexts = (uuid == "all") 540 if not load_all_kexts and len(uuid_regex.findall(uuid)) == 0: 541 raise ArgumentError("Unknown argument {:s}".format(uuid)) 542 543 pool = concurrent.futures.ThreadPoolExecutor() 544 for sum in GetAllKextSummaries(): 545 cur_uuid = sum.uuid.lower() 546 if load_all_kexts or (uuid == cur_uuid): 547 if kernel_uuid != cur_uuid: 548 pool.submit(FetchDSYM, sum) 549 pool.shutdown(wait=True) 550 551 kern.symbolicator = None 552 553 554@lldb_command('addkext', 'AF:T:N:') 555def AddKextSyms(cmd_args=[], cmd_options={}): 556 """ Add kext symbols into lldb. 557 This command finds symbols for a uuid and load the required executable 558 Usage: 559 addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E 560 addkext -F <abs/path/to/executable> : Load kext with executable 561 addkext -F <abs/path/to/executable> <load_address> : Load kext with executable at specified load address 562 addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto 563 addkext -N <name> -A: Load all kext that matches the name provided. eg. to load all kext with Apple in name do (lldb) addkext -N Apple -A 564 addkext -T <thread>: Given a thread, load all kexts needed to symbolicate its backtrace 565 addkext all : Will load all the kext symbols - SLOW 566 """ 567 568 # Load kext by file name. 569 if "-F" in cmd_options: 570 exec_path = cmd_options["-F"] 571 exec_full_path = ResolveFSPath(exec_path) 572 if not os.path.exists(exec_full_path): 573 raise ArgumentError("Unable to resolve {:s}".format(exec_path)) 574 575 if not os.path.isfile(exec_full_path): 576 raise ArgumentError( 577 """Path is {:s} not a filepath. 578 Please check that path points to executable. 579 For ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub. 580 Note: LLDB does not support adding kext based on directory paths like gdb used to.""".format(exec_path)) 581 582 slide_value = None 583 if cmd_args: 584 slide_value = cmd_args[0] 585 debuglog("loading slide value from user input {:s}".format(cmd_args[0])) 586 587 return AddKextSymsByFile(exec_full_path, slide_value) 588 589 # Load kext by name. 590 if "-N" in cmd_options: 591 kext_name = cmd_options["-N"] 592 return AddKextSymsByName(kext_name, "-A" in cmd_options) 593 594 # Load all kexts needed to symbolicate a thread's backtrace 595 if "-T" in cmd_options: 596 return AddKextByThread(cmd_options["-T"]) 597 598 # Load kexts by UUID or "all" 599 if len(cmd_args) < 1: 600 raise ArgumentError("No arguments specified.") 601 602 uuid = cmd_args[0].lower() 603 return AddKextByUUID(uuid) 604 605 606@lldb_command('addkextaddr') 607def AddKextAddr(cmd_args=[]): 608 """ Given an address, load the kext which contains that address 609 Syntax: (lldb) addkextaddr <addr> 610 """ 611 if len(cmd_args) < 1: 612 raise ArgumentError("Insufficient arguments") 613 614 addr = ArgumentStringToInt(cmd_args[0]) 615 AddKextByAddress(addr) 616 617 618class KextMemoryObject(kmemory.MemoryObject): 619 """ Describes an object landing in some kext """ 620 621 MO_KIND = "kext mach-o" 622 623 def __init__(self, kmem, address, kinfo): 624 super().__init__(kmem, address) 625 self.kinfo = kinfo 626 self.target = kmem.target 627 628 @property 629 def object_range(self): 630 seg = next(seg_contains(self.kinfo.segments, self.address)) 631 sec = next(sec_contains(seg.sections, self.address), None) 632 if sec: 633 return kmemory.MemoryRange(sec.addr, sec.addr + sec.size) 634 return kmemory.MemoryRange(seg.vmaddr, seg.vmaddr + seg.vmsize) 635 636 def find_mod_seg_sect(self): 637 target = self.target 638 address = self.address 639 640 return next(( 641 (module, segment, next(sbsec_contains(target, segment, address), None)) 642 for module in target.module_iter() 643 for segment in sbsec_contains(target, module.section_iter(), address) 644 ), (None, None, None)) 645 646 def describe(self, verbose=False): 647 from lldb.utils.symbolication import Symbolicator 648 649 addr = self.address 650 kinfo = self.kinfo 651 652 sbmod, sbseg, sbsec = self.find_mod_seg_sect() 653 if sbmod is None: 654 FetchDSYM(kinfo) 655 print() 656 sbmod, sbseg, sbsec = self.find_mod_seg_sect() 657 658 syms = Symbolicator.InitWithSBTarget(self.target).symbolicate(addr) 659 sym = next(iter(syms)) if syms else None 660 661 if not sbseg: 662 # not really an SBSection but we only need to pretty print 'name' 663 # which both have, yay duck typing 664 sbseg = next(seg_contains(kinfo.segments, addr), None) 665 666 fmt = "Kext Symbol Info\n" 667 fmt += " kext : {kinfo.name} ({kinfo.uuid})\n" 668 fmt += " module : {sbmod.file.basename}\n" if sbmod else "" 669 fmt += " section : {sbseg.name} {sbsec.name}\n" if sbsec else \ 670 " segment : {sbseg.name}\n" if sbseg else "" 671 fmt += " symbol : {sym!s}\n" if sym else "" 672 673 print(fmt.format(kinfo=kinfo, sbmod=sbmod, sbseg=sbseg, sbsec=sbsec, sym=sym)) 674 675 676class MainBinaryMemoryObject(kmemory.MemoryObject): 677 """ Describes an object landing in the main kernel binary """ 678 679 MO_KIND = "kernel mach-o" 680 681 def __init__(self, kmem, address, section): 682 super().__init__(kmem, address) 683 self.section = section 684 self.target = kmem.target 685 686 def _subsection(self): 687 return next(sbsec_contains(self.target, self.section, self.address), None) 688 689 @property 690 def object_range(self): 691 target = self.target 692 section = self._subsection() or self.section 693 addr = section.GetLoadAddress(target) 694 size = section.GetByteSize() 695 return kmemory.MemoryRange(addr, addr + size) 696 697 @property 698 def module(self): 699 return self.target.GetModuleAtIndex(0).GetFileSpec().GetFilename() 700 701 @property 702 def uuid(self): 703 return self.target.GetModuleAtIndex(0).GetUUIDString() 704 705 def describe(self, verbose=False): 706 from lldb.utils.symbolication import Symbolicator 707 708 subsec = self._subsection() 709 syms = Symbolicator.InitWithSBTarget(self.target).symbolicate(self.address) 710 sym = next(iter(syms)) if syms else None 711 712 fmt = "Symbol Info\n" 713 fmt += " module : {mo.module}\n" 714 fmt += " uuid : {mo.uuid}\n" 715 fmt += " section : {mo.section.name} {subsec.name}\n" if subsec else "" 716 fmt += " segment : {mo.section.name}\n" if not subsec else "" 717 fmt += " symbol : {sym}\n" if sym else "" 718 719 print(fmt.format(mo=self, subsec=subsec, sym=sym)) 720 721 722@kmemory.whatis_provider 723class KextWhatisProvider(kmemory.WhatisProvider): 724 """ Kext ranges whatis provider """ 725 726 COST = 100 727 728 def claims(self, address): 729 target = self.target 730 mainmod = target.GetModuleAtIndex(0) 731 732 # 733 # TODO: surely the kexts can provide a better range check 734 # 735 736 return any( 737 sbsec_contains(target, mainmod.section_iter(), address) 738 ) or any( 739 any(seg_contains(kinfo.segments, address)) 740 for kinfo in GetAllKextSummaries() 741 ) 742 743 def lookup(self, address): 744 target = self.target 745 mainmod = target.GetModuleAtIndex(0) 746 747 section = next(sbsec_contains(target, mainmod.section_iter(), address), None) 748 749 if section: 750 return MainBinaryMemoryObject(self.kmem, address, section) 751 752 return KextMemoryObject(self.kmem, address, next( 753 kinfo 754 for kinfo in GetAllKextSummaries() 755 if any(seg_contains(kinfo.segments, address)) 756 )) 757 758 759# Aliases for backward compatibility. 760 761lldb_alias('showkmod', 'showkmodaddr') 762lldb_alias('showkext', 'showkmodaddr') 763lldb_alias('showkextaddr', 'showkmodaddr') 764lldb_alias('showallkexts', 'showallkmods') 765