Created
September 21, 2025 22:07
-
-
Save PsychoTea/4f2698a70fd1386f6a8465ee987b1404 to your computer and use it in GitHub Desktop.
A small Python script using r2 to fix up adrp/add instructions which become invalid after creating a kernel cache with kmutil.
This file contains hidden or 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
| #!/usr/bin/env python3 | |
| import argparse | |
| from dataclasses import dataclass | |
| import re | |
| import shutil | |
| import r2pipe | |
| @dataclass | |
| class Region: | |
| name: str | |
| start: int | |
| end: int | |
| def find_text_exec_regions(r2: r2pipe.open) -> list[Region]: | |
| ranges: list[Region] = [] | |
| for section in r2.cmdj("iSj") or []: | |
| if not (name := section.get("name")): | |
| raise ValueError("failed to get section name") | |
| if "__TEXT_EXEC" not in name: | |
| continue # Only want TEXT_EXEC segments. | |
| if "__text" not in name: | |
| continue # Only want the actual code in those segments. | |
| if "com.apple.kernel" in name: | |
| continue # Don't need to do fixups in the kernel itself. | |
| if not (start := section.get("vaddr")): | |
| raise ValueError(f"failed to get section start: {name}") | |
| if not (size := section.get("vsize")): | |
| raise ValueError(f"failed to get section size: {name}") | |
| ranges.append(Region(name, start, start + size)) | |
| return ranges | |
| def r2_set_search_range(r2: r2pipe.open, start: int, end: int): | |
| r2.cmd("e search.flags=false") | |
| r2.cmd("e search.align=4") | |
| r2.cmd(f"e search.from={start:#x}") | |
| r2.cmd(f"e search.to={end:#x}") | |
| ADRP_REGEX = re.compile( | |
| r"adrp (x[0-9]+), ([0-9xa-f]+); add (x[0-9]+), (x[0-9]+), (0x[0-9xa-f]+)" | |
| ) | |
| @dataclass | |
| class ADRL: | |
| pc: int # Address of the instruction | |
| page: int # Target page | |
| offset: int # Target page offset/index | |
| reg: str # Register used | |
| asm: list[str] # Assembly lines | |
| @property | |
| def target(self) -> int: | |
| return self.page + self.offset | |
| @classmethod | |
| def from_r2_search_json(cls, insn_json) -> "ADRL | None": | |
| if not (matches := re.findall(ADRP_REGEX, insn_json["code"])): | |
| return None | |
| reg1, page, reg2, reg3, offset = matches[0] | |
| if reg1 != reg2 != reg3: | |
| return None | |
| asm = [a.strip() for a in insn_json["code"].split(";")] | |
| return cls(insn_json["addr"], int(page, 16), int(offset, 16), reg1, asm) | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("infile", help="input file") | |
| parser.add_argument("outfile", help="output file") | |
| args = parser.parse_args() | |
| shutil.copyfile(args.infile, args.outfile) # Since r2 writes in-place. | |
| r2 = r2pipe.open(args.outfile) | |
| r2.cmd("oo+") # Enable writing to the input (output) file. | |
| for fr in find_text_exec_regions(r2): | |
| adjustment = fr.start & 0xFFF | |
| print(f"Fixing range: {fr.name} ({fr.start:#x}-{fr.end:#x}) (+{adjustment:#x})") | |
| r2_set_search_range(r2, fr.start, fr.end) | |
| adrls = r2.cmdj('"/adj adrp; add"') or [] | |
| for adrl_json in adrls: | |
| if not (adrl := ADRL.from_r2_search_json(adrl_json)): | |
| continue | |
| target_seg_json = r2.cmdj(f"iSj. @ {adrl.target:#x}") or {} | |
| target_seg_name = target_seg_json.get("name") or "" | |
| if "__TEXT_EXEC.__text" not in target_seg_name: | |
| continue # ADRL doesn't point inside TEXT_EXEC segment. | |
| print(f"Found ADRL at {adrl.pc:#x}:") | |
| new_target = adrl.target + adjustment | |
| adrp_value = new_target & ~0xfff | |
| add_value = new_target & 0xfff | |
| adrp_line = f"adrp {adrl.reg}, {adrp_value:#x}" | |
| add_line = f"add {adrl.reg}, {adrl.reg}, {add_value:#x}" | |
| print(f" Original code: {'; '.join(adrl.asm)}") | |
| print(f" Replaced code: {adrp_line}; {add_line}") | |
| r2.cmd(f"s {adrl.pc:#x}") | |
| r2.cmd(f'"wa {adrp_line}; {add_line}"') | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment