+ return None
+
+@header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object"))
+@lldb_command('vmpagelookup')
+def VMPageLookup(cmd_args=None):
+ """ Print the pages in the page bucket corresponding to the provided object and offset.
+ Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t>
+ """
+ if cmd_args == None or len(cmd_args) < 2:
+ raise ArgumentError("Please specify an object and offset.")
+ format_string = "{0: <#020x} {1: <#020x} {2: <#020x}\n"
+
+ obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
+ off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
+
+ hash_id = _calc_vm_page_hash(obj, off)
+
+ page_list = kern.globals.vm_page_buckets[hash_id].page_list
+ print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(page_list)))
+
+ print VMPageLookup.header
+ page = _vm_page_unpack_ptr(page_list)
+ while (page != 0) :
+ pg_t = kern.GetValueFromAddress(page, 'vm_page_t')
+ print format_string.format(page, pg_t.offset, pg_t.object)
+ page = _vm_page_unpack_ptr(pg_t.next_m)
+
+def _vm_page_unpack_ptr(page):
+ if kern.ptrsize == 4 :
+ return page
+
+ if page == 0 :
+ return page
+
+ min_addr = kern.globals.vm_min_kernel_and_kext_address
+ #INTEL - min_addr = 0xffffff7f80000000
+ #ARM - min_addr = 0x80000000
+ #ARM64 - min_addr = 0xffffff8000000000
+ return ((page << 6) + min_addr)
+
+@lldb_command('calcvmpagehash')
+def CalcVMPageHash(cmd_args=None):
+ """ Get the page bucket corresponding to the provided object and offset.
+ Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t>
+ """
+ if cmd_args == None or len(cmd_args) < 2:
+ raise ArgumentError("Please specify an object and offset.")
+
+ obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
+ off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
+
+ hash_id = _calc_vm_page_hash(obj, off)
+
+ print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(kern.globals.vm_page_buckets[hash_id].page_list)))
+ return None
+
+def _calc_vm_page_hash(obj, off):
+ bucket_hash = (int) (kern.globals.vm_page_bucket_hash)
+ hash_mask = (int) (kern.globals.vm_page_hash_mask)
+
+ one = (obj * bucket_hash) & 0xFFFFFFFF
+ two = off >> unsigned(kern.globals.page_shift)
+ three = two ^ bucket_hash
+ four = one + three
+ hash_id = four & hash_mask
+
+ return hash_id
+
+@header("{0: <10s} of {1: <10s} {2: <20s} {3: <20s} {4: <20s} {5: <10s} {6: <5s}\t {7: <28s}\t{8: <50s}".format("index", "total", "vm_page_t", "offset", "next", "phys_page", "wire#", "first bitfield", "second bitfield"))
+@lldb_command('vmobjectwalkpages', 'SBNQP:')
+def VMObjectWalkPages(cmd_args=None, cmd_options={}):
+ """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we
+ specifically look for this page, highlighting it in the output or noting if it was not found. For
+ each page, we confirm that it points to the object. We also keep track of the number of pages we
+ see and compare this to the object's resident page count field.
+ Usage:
+ vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default)
+ vmobjectwalkpages <vm_object_t> -B : Walk and print all the pages for a given object (up to 4K pages by default), traversing the memq backwards
+ vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit
+ vmobjectwalkpages <vm_object_t> -Q : Walk all pages for a given object, looking for known signs of corruption (i.e. inactive and active both being set for a page)
+ vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with ***
+ vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page
+
+ """
+
+ if (cmd_args == None or len(cmd_args) < 1):
+ raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t")
+
+ out_string = ""
+
+ obj = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
+
+ page = 0
+ if "-P" in cmd_options:
+ page = kern.GetValueFromAddress(cmd_options['-P'], 'vm_page_t')
+
+ stop = 0
+ if "-S" in cmd_options:
+ if page == 0:
+ raise ArgumentError("-S can only be passed when a page is specified with -P")
+ stop = 1
+
+ walk_backwards = False
+ if "-B" in cmd_options:
+ walk_backwards = True
+
+ quiet_mode = False
+ if "-Q" in cmd_options:
+ quiet_mode = True
+
+ if not quiet_mode:
+ print VMObjectWalkPages.header
+ format_string = "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t"
+ first_bitfield_format_string = "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:"
+ first_bitfield_format_string += "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}"
+ second_bitfield_format_string = first_bitfield_format_string
+ second_bitfield_format_string += ":{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:"
+ second_bitfield_format_string += "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}\n"
+ first_bitfield_format_string += "\t"
+
+ limit = 4096 #arbitrary limit of number of pages to walk
+ ignore_limit = 0
+ if "-N" in cmd_options:
+ ignore_limit = 1
+
+ page_count = 0
+ res_page_count = unsigned(obj.resident_page_count)
+ page_found = False
+ pages_seen = set()
+
+ for vmp in IterateQueue(obj.memq, "vm_page_t", "listq", walk_backwards):
+ page_count += 1
+ out_string = ""
+ if (page != 0 and not(page_found) and vmp == page):
+ out_string += "******"
+ page_found = True
+
+ if page != 0 or quiet_mode:
+ if (page_count % 1000) == 0:
+ print "traversed %d pages ...\n" % (page_count)
+ else:
+ out_string += format_string.format(page_count, res_page_count, vmp, vmp.offset, vmp.listq.next, vmp.phys_page, vmp.wire_count)
+ out_string += first_bitfield_format_string.format(vmp.active, vmp.inactive, vmp.clean_queue, vmp.local, vmp.speculative,
+ vmp.throttled, vmp.free, vmp.pageout_queue, vmp.laundry, vmp.reference,
+ vmp.gobbled, vmp.private, vmp.no_cache)
+
+ out_string += second_bitfield_format_string.format(vmp.busy, vmp.wanted, vmp.tabled, vmp.hashed, vmp.fictitious, vmp.clustered,
+ vmp.clustered, vmp.pmapped, vmp.xpmapped, vmp.wpmapped, vmp.pageout, vmp.absent,
+ vmp.error, vmp.dirty, vmp.cleaning, vmp.precious, vmp.precious, vmp.overwriting,
+ vmp.restart, vmp.unusual, vmp.encrypted, vmp.encrypted, vmp.encrypted_cleaning,
+ vmp.cs_validated, vmp.cs_tainted, vmp.cs_nx, vmp.reusable, vmp.lopage, vmp.slid, vmp.compressor,
+ vmp.written_by_kernel)
+
+ if (vmp in pages_seen):
+ print out_string + "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " twice. stopping...\n"
+ return
+
+ if (vmp.object != obj):
+ print out_string + " vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " points to different vm_object_t: " + "{0: <#020x}".format(unsigned(vmp.object))
+ return
+
+ if (not vmp.local) and (vmp.wire_count > 0):
+ if (vmp.active or vmp.inactive or vmp.speculative or vmp.throttled or vmp.pageout_queue):
+ print out_string + " wired page with wrong page queue attributes\n"
+ print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " active: %d inactive: %d speculative: %d throttled %d pageout_queue: %d\n" % (vmp.active,
+ vmp.inactive, vmp.speculative, vmp.throttled, vmp.pageout_queue)
+ print "stopping...\n"
+ return
+
+ if ((vmp.free + vmp.active + vmp.inactive + vmp.speculative + vmp.throttled + vmp.pageout_queue) > 1):
+ print out_string + " more than one pageout queue bit set active\n"
+ print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " free: %d active: %d inactive: %d speculative: %d throttled: %d pageout_queue: %d\n" % (vmp.free,
+ vmp.active, vmp.inactive, vmp.speculative, vmp.throttled, vmp.pageout_queue)
+ print "stopping...\n"
+ return
+
+ if ((vmp.__unused_pageq_bits != 0) or (vmp.__unused_object_bits != 0)):
+ print out_string + " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " unused__pageq_bits: %d unused_object_bits : %d\n" % (vmp.__unused_pageq_bits,
+ vmp.__unused_object_bits)
+ print "stopping...\n"
+ return
+
+ pages_seen.add(vmp)
+
+ if False:
+ hash_id = _calc_vm_page_hash(obj, vmp.offset)
+ hash_page_list = kern.globals.vm_page_buckets[hash_id].page_list
+ hash_page = _vm_page_unpack_ptr(hash_page_list)
+ hash_page_t = 0
+
+ while (hash_page != 0):
+ hash_page_t = kern.GetValueFromAddress(hash_page, 'vm_page_t')
+ if hash_page_t == vmp:
+ break
+ hash_page = _vm_page_unpack_ptr(hash_page_t.next_m)
+
+ if (unsigned(vmp) != unsigned(hash_page_t)):
+ print out_string + "unable to find page: " + "{0: <#020x}".format(unsigned(vmp)) + " from object in kernel page bucket list\n"
+ print lldb_run_command("vm_page_info %s 0x%x" % (cmd_args[0], unsigned(vmp.offset)))
+ return
+
+ if (page_count >= limit and not(ignore_limit)):
+ print out_string + "Limit reached (%d pages), stopping..." % (limit)
+ return
+
+ print out_string
+
+ if page_found and stop:
+ print("Object reports resident page count of: %d we stopped after traversing %d and finding the requested page.\n" % (unsigned(obj.res_page_count), unsigned(page_count)))
+ return
+
+ if (page != 0):
+ print("page found? : %s\n" % page_found)
+
+ print("Object reports resident page count of %d, we saw %d pages when we walked the resident list.\n" % (unsigned(obj.resident_page_count), unsigned(page_count)))
+
+
+@lldb_command("show_all_apple_protect_pagers")
+def ShowAllAppleProtectPagers(cmd_args=None):
+ """Routine to print all apple_protect pagers
+ usage: show_all_apple_protect_pagers
+ """
+ print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "mo_control", "object", "offset", "crypto_offset", "crypto_start", "crypto_end")
+ qhead = kern.globals.apple_protect_pager_queue
+ qtype = GetType('apple_protect_pager *')
+ qcnt = kern.globals.apple_protect_pager_count
+ idx = 0
+ for pager in IterateQueue(qhead, qtype, "pager_queue"):
+ idx = idx + 1
+ show_apple_protect_pager(pager, qcnt, idx)
+
+@lldb_command("show_apple_protect_pager")
+def ShowAppleProtectPager(cmd_args=None):
+ """Routine to print out info about an apple_protect pager
+ usage: show_apple_protect_pager <pager>
+ """
+ if cmd_args == None or len(cmd_args) < 1:
+ print "Invalid argument.", ShowMap.__doc__
+ return
+ pager = kern.GetValueFromAddress(cmd_ars[0], 'apple_protect_pager_t')
+ show_apple_protect_pager(pager, 1, 1)
+
+def show_apple_protect_pager(pager, qcnt, idx):
+ object = pager.backing_object
+ shadow = object.shadow
+ while shadow != 0:
+ object = shadow
+ shadow = object.shadow
+ vnode_pager = Cast(object.pager,'vnode_pager *')
+ filename = GetVnodePath(vnode_pager.vnode_handle)
+ print "{:>3}/{:<3d} {:#018x} {:>5d} {:>5d} {:>6d} {:#018x} {:#018x} {:#018x} {:#018x} {:#018x} {:#018x}\n\tcrypt_info:{:#018x} <decrypt:{:#018x} end:{:#018x} ops:{:#018x} refs:{:<d}>\n\tvnode:{:#018x} {:s}\n".format(idx, qcnt, pager, pager.ref_count, pager.is_ready, pager.is_mapped, pager.pager_control, pager.backing_object, pager.backing_offset, pager.crypto_backing_offset, pager.crypto_start, pager.crypto_end, pager.crypt_info, pager.crypt_info.page_decrypt, pager.crypt_info.crypt_end, pager.crypt_info.crypt_ops, pager.crypt_info.crypt_refcnt, vnode_pager.vnode_handle, filename)