Files

94 lines
3.2 KiB
Python

#!/usr/bin/env python3
import subprocess, os, glob, sys, time, struct
proc = None
RPP = sys.argv[1] if len(sys.argv) > 1 else "/home/m/soothe-bt/render_long.rpp"
START = len(sys.argv) > 2 and sys.argv[2] == "start"
if START:
proc = subprocess.Popen(
['reaper', '-nosplash', '-renderproject', RPP],
stdout=open('/tmp/rtfind.log', 'w'), stderr=subprocess.STDOUT)
print('reaper', proc.pid, flush=True)
# The SpectralProcessor vtable slot containing ctor 0x180529610
CTOR = 0x180529610
# candidate vptrs: slots near it
VCANDS = [0x1824abb80, 0x1824abb88, 0x1824abb90, 0x1824abb98, 0x1824abba0, 0x1824abba8, 0x1824abbb0]
def all_mapped(pid):
out = []
maps = open(f'/proc/{pid}/maps').read()
for line in maps.splitlines():
p = line.split()
lo, hi = (int(x, 16) for x in p[0].split('-'))
if 'r' in p[1]:
out.append((lo, hi))
return out
def scan(pid, vptr, tgt):
mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
hits = []
for lo, hi in all_mapped(pid):
try:
d = os.pread(mem, hi - lo, lo)
except Exception:
continue
i = 0
while True:
i = d.find(tgt, i)
if i < 0:
break
hits.append(lo + i)
i += 1
os.close(mem)
return hits
def filter_candidates(pid, hit_addrs):
# for each candidate object addr (addr = where vptr stored => object base), read fields
out = []
mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
for a in hit_addrs:
try:
d = os.pread(mem, 0x600, a)
except Exception:
continue
f4 = struct.unpack('<%dI' % (len(d) // 4), d)
# look for N at +0x19c and oversample at +0x1ac
n = f4[0x19c // 4] if (0x19c // 4) < len(f4) else 0
u = f4[0x1ac // 4] if (0x1ac // 4) < len(f4) else 0
w = struct.unpack('<d', d[0x1a4:0x1a4 + 8])[0] if len(d) >= 0x1a4 + 8 else 0
ok = (n in (256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536) and
u in (2, 4, 8, 16))
if w != 0 and (n > 0 or ok):
out.append((a, n, u, w, ok))
os.close(mem)
return out
hosts = {}
t0 = time.time()
seen = set()
while time.time() - t0 < 90:
for p in glob.glob('/proc/[0-9]*'):
try:
pid = int(os.path.basename(p))
if pid in seen:
continue
maps = open(f'/proc/{pid}/maps').read()
if 'soothe2' not in maps or 'yabridge-host.exe' not in open(f'/proc/{pid}/cmdline', 'rb').read().decode('utf8', 'replace'):
continue
except Exception:
continue
seen.add(pid)
print('host', pid, flush=True)
# scan whole maps for each candidate vptr
for v in VCANDS:
h = scan(pid, v, struct.pack('<Q', v))
if h:
res = filter_candidates(pid, h)
print(' vptr', hex(v), 'hits', len(h), 'obj-cands', len(res), flush=True)
for r in res[:10]:
print(' obj', hex(r[0]), 'N', r[1], 'ups', r[2], 'win', r[3], 'OK' if r[4] else '', flush=True)
time.sleep(0.4)
if proc:
proc.kill()
print('done', flush=True)