Created
June 20, 2018 19:37
-
-
Save bazad/e11b259855a8ff6195ba17fc35bbc532 to your computer and use it in GitHub Desktop.
Working with the new iOS 12 kernelcache's tagged pointers in IDA.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# ios-12-kernelcache-tagged-pointers.py | |
# Brandon Azad | |
# | |
# An idapython script that shows how to work with the new tagged pointers in the iOS 12 | |
# kernelcache. | |
# | |
import idc | |
import idaapi | |
import idautils | |
import random | |
from collections import defaultdict, Counter | |
def untag_pointer(tp): | |
return tp | 0xffff000000000000 | |
def tagged_pointer_tag(tp): | |
return (tp >> 48) & 0xffff | |
def is_tagged_pointer_format(pointer): | |
return (pointer & 0x0000FFFFf0000000) == 0x0000FFF000000000 | |
def is_valid_tagged_pointer(pointer): | |
return is_tagged_pointer_format(pointer) and idaapi.getseg(untag_pointer(pointer)) | |
def tagged_pointer_tag_to_span(tag): | |
return (tag >> 1) & ~0x3 | |
def tagged_pointer_span(tp): | |
return tagged_pointer_tag_to_span(tagged_pointer_tag(tp)) | |
def tagged_pointers(): | |
if tagged_pointers.eas_and_tps is None: | |
eas_and_tps = [] | |
for seg in idautils.Segments(): | |
start = idc.SegStart(seg) | |
end = idc.SegEnd(seg) | |
for ea in range(start, end, 4): | |
value = idc.Qword(ea) | |
if is_valid_tagged_pointer(value): | |
eas_and_tps.append((ea, value)) | |
tagged_pointers.eas_and_tps = eas_and_tps | |
return tagged_pointers | |
tagged_pointers.eas_and_tps = None | |
def print_tagged_pointer_counts_per_section(): | |
seg_to_tag_count = Counter() | |
for ea, tp in tagged_pointers().eas_and_tps: | |
if tagged_pointer_tag(tp) == 0xffff: | |
continue | |
seg = idc.SegStart(ea) | |
seg_to_tag_count[seg] += 1 | |
segs = list(seg_to_tag_count.keys()) | |
segs.sort() | |
for seg in segs: | |
print '{:32s} {:8d}'.format(idc.SegName(seg), seg_to_tag_count[seg]) | |
def print_untagged_pointer_counts_per_section(): | |
seg_to_tag_count = Counter() | |
for ea, tp in tagged_pointers().eas_and_tps: | |
if tagged_pointer_tag(tp) != 0xffff: | |
continue | |
seg = idc.SegStart(ea) | |
seg_to_tag_count[seg] += 1 | |
segs = list(seg_to_tag_count.keys()) | |
segs.sort() | |
for seg in segs: | |
print '{:32s} {:8d}'.format(idc.SegName(seg), seg_to_tag_count[seg]) | |
def print_tagged_pointer_counts_by_tag_per_section(): | |
seg_to_tag_counts = defaultdict(Counter) | |
for ea, tp in tagged_pointers().eas_and_tps: | |
seg = idc.SegStart(ea) | |
tag = tagged_pointer_tag(tp) | |
seg_to_tag_counts[seg][tag] += 1 | |
segs = list(seg_to_tag_counts.keys()) | |
segs.sort() | |
for seg in segs: | |
tag_counts = seg_to_tag_counts[seg] | |
tags = list(tag_counts.keys()) | |
tags.sort() | |
print '{:32s} {}'.format(idc.SegName(seg), | |
', '.join('{:04x} ({})'.format(tag, tag_counts[tag]) for tag in tags)) | |
def print_references_for_tagged_pointers(): | |
tag_to_eas_and_tps = defaultdict(list) | |
for ea, tp in tagged_pointers().eas_and_tps: | |
tag_to_eas_and_tps[tagged_pointer_tag(tp)].append((ea, tp)) | |
tags = list(tag_to_eas_and_tps.keys()) | |
tags.sort() | |
for tag in tags: | |
eas_and_tps = tag_to_eas_and_tps[tag] | |
ea, tp = random.choice(eas_and_tps) | |
kp = untag_pointer(tp) | |
print '{:04x} {:8d} {:016x} {:32s} -> {:016x} {:s}'.format( | |
tag, len(eas_and_tps), ea, idc.SegName(ea), kp, idc.SegName(kp)) | |
def find_tagged_pointers_to_address(address): | |
tag_to_eas = defaultdict(list) | |
for ea, tp in tagged_pointers().eas_and_tps: | |
if untag_pointer(tp) == address: | |
tag_to_eas[tagged_pointer_tag(tp)].append(ea) | |
return dict(tag_to_eas) | |
def print_tagged_pointers_to_address(address): | |
tag_to_eas = find_tagged_pointers_to_address(address) | |
tags = list(tag_to_eas.keys()) | |
tags.sort() | |
for tag in tags: | |
eas = tag_to_eas[tag] | |
print '{:04x} {:8d} {}'.format( | |
tag, len(eas), ', '.join('{:016x}'.format(ea) for ea in eas)) | |
def check_pointer_tags_as_links(): | |
last_seg, last_ea, last_tp = None, None, None | |
correct, warnings, errors, skipped = 0, 0, 0, 0 | |
for ea, tp in tagged_pointers().eas_and_tps: | |
if tagged_pointer_tag(tp) == 0xffff: | |
skipped += 1 | |
continue | |
seg = idc.SegStart(ea) | |
if seg == last_seg: | |
span = tagged_pointer_span(last_tp) | |
if span == 0: | |
real_span = ea - last_ea | |
print 'WARNING: ZERO SPAN LINK: ea={:016x}, tp={:016x}, last_ea={:016x}, last_tp={:016x}, span={:04x}'.format( | |
ea, tp, last_ea, last_tp, real_span) | |
warnings += 1 | |
elif ea != last_ea + span: | |
print 'ERROR: BAD LINK: ea={:016x}, tp={:016x}, last_ea={:016x}, last_tp={:016x}'.format( | |
ea, tp, last_ea, last_tp) | |
errors += 1 | |
break | |
else: | |
correct += 1 | |
elif last_ea: | |
span = tagged_pointer_span(last_tp) | |
if span == 0: | |
correct += 1 | |
else: | |
print 'WARNING: NON-ZERO SPAN LINK AT END OF SEGMENT: ea={:016x}, tp={:016x}, last_ea={:016x}, last_tp={:016x}, seg={:016x}'.format( | |
ea, tp, last_ea, last_tp, seg) | |
warnings += 1 | |
last_seg, last_ea, last_tp = seg, ea, tp | |
print '{} correct, {} warnings, {} errors, {} skipped'.format( | |
correct, warnings, errors, skipped) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment