94 lines
3.2 KiB
Python
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) |