re: LIVE N0@44100 = 2048, oversample 4 (two SP instances agree); angle-ramp+Cody-Waite twiddle path confirmed for main FFT; rtone/rtver scripts
This commit is contained in:
+19
@@ -192,3 +192,22 @@
|
||||
182617828/850/878 ALL currently point to 0x18014ec20 (same fn).
|
||||
- CONSEQUENCE: for 44100 => N=N0; if N0<=1024 the twiddle = static sin table (reproducible directly);
|
||||
if N0>1024 must replicate Cody-Waite sin/cos of FUN_18014ec20 bit-exactly. N0 (+0x19c) still unfound statically.
|
||||
|
||||
### N0 RESOLVED - LIVE DUMP @44100 (2026-08-17) — KEY
|
||||
- Soothe2 render_short.rpp (44100 Hz) + /proc/pid/mem scan of yabridge-host heap
|
||||
(out file growth confirmed processing; targeted field-signature scan:
|
||||
qword-aligned, +0x19c == +0x1a0 == pow2 in [256..65536], +0x1ac in {2,4,8,16}).
|
||||
- Two independent live SP instances (fork L/R, vptr 0x1824ac210 / 0x1824ac7a8,
|
||||
vtbl-slot families 0x18052xxx = SpectralProcessor) BOTH report:
|
||||
+0x19c N0 = 2048, +0x1a0 = 2048 (=N0, since sr==44100 => 2^floor(log2(1))=1)
|
||||
+0x1a4 winscale = 0.49999988 (Hann 0.5) / 0.66666639 (2/3 window)
|
||||
+0x1ac oversample = 4
|
||||
- N0=2048 > 1024 => twiddle goes the ANGLE-RAMP branch of FUN_180039b00
|
||||
(buf[k]=k*(2π/N) then sin/cos via thunk 1801de760 / thunk 1801e3f20),
|
||||
and cplx-stage loader FUN_18014ec20 re-derives re/im (Dekker-split + Cody-Waite).
|
||||
- Implication for reimpl: for 44.1k we MUST reproduce Cody-Waite sin/cos
|
||||
(1/π=0.31830988618379069, magic 6755399441055744, π_hi/π_lo reduction,
|
||||
poly -1/3!..-1/15!), NOT the static 1024-table path. Soothing does NOT use the
|
||||
static sin-table for its main FFT at 44.1k.
|
||||
- Method: rtone.py (single render, wait for out growth, scan), rtver.py (field verify).
|
||||
Old soothe_rt.bin is module-only range => heap objects absent (why vptr-scan found 0).
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess, os, glob, sys, time, struct
|
||||
|
||||
RPP = "/home/m/soothe-bt/render_long.rpp"
|
||||
proc = subprocess.Popen(
|
||||
['reaper', '-nosplash', '-renderproject', RPP],
|
||||
stdout=open('/tmp/rtall.log', 'w'), stderr=subprocess.STDOUT)
|
||||
print('reaper', proc.pid, flush=True)
|
||||
|
||||
start, end = 0x1824abb00, 0x1824abd00 # vtbl region
|
||||
SLOTS = list(range(start, end, 8))
|
||||
|
||||
|
||||
def find_hosts():
|
||||
out = []
|
||||
for p in glob.glob('/proc/[0-9]*'):
|
||||
try:
|
||||
pid = int(os.path.basename(p))
|
||||
m = open(f'/proc/{pid}/maps').read()
|
||||
c = open(f'/proc/{pid}/cmdline', 'rb').read().decode('utf8', 'replace')
|
||||
if 'soothe2' in m and 'yabridge-host.exe' in c:
|
||||
out.append(pid)
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def scan_all(pid):
|
||||
mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
|
||||
resc = {s: [] for s in SLOTS}
|
||||
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' not in p[1]:
|
||||
continue
|
||||
try:
|
||||
d = os.pread(mem, hi - lo, lo)
|
||||
except Exception:
|
||||
continue
|
||||
for s in SLOTS:
|
||||
t = struct.pack('<Q', s)
|
||||
i = 0
|
||||
while True:
|
||||
i = d.find(t, i)
|
||||
if i < 0:
|
||||
break
|
||||
resc[s].append(lo + i)
|
||||
i += 1
|
||||
os.close(mem)
|
||||
return resc
|
||||
|
||||
results = {}
|
||||
t0 = time.time()
|
||||
while time.time() - t0 < 75:
|
||||
for pid in find_hosts():
|
||||
if pid in results:
|
||||
continue
|
||||
r = scan_all(pid)
|
||||
results[pid] = r
|
||||
print('PID', pid, flush=True)
|
||||
for s in SLOTS:
|
||||
if r[s]:
|
||||
print(f' {s:#x} hits={len(r[s])} @ {[hex(x) for x in r[s][:3]]}', flush=True)
|
||||
time.sleep(0.4)
|
||||
proc.kill()
|
||||
print('done', flush=True)
|
||||
@@ -0,0 +1,94 @@
|
||||
#!/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)
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess, os, glob, sys, time, struct
|
||||
|
||||
RPP = "/home/m/soothe-bt/render_long.rpp"
|
||||
proc = subprocess.Popen(['reaper', '-nosplash', '-renderproject', RPP],
|
||||
stdout=open('/tmp/rtdeep2.log', 'w'), stderr=subprocess.STDOUT)
|
||||
print('reaper', proc.pid, flush=True)
|
||||
|
||||
|
||||
def find_hosts():
|
||||
out = []
|
||||
for p in glob.glob('/proc/[0-9]*'):
|
||||
try:
|
||||
pid = int(os.path.basename(p))
|
||||
m = open(f'/proc/{pid}/maps').read()
|
||||
c = open(f'/proc/{pid}/cmdline', 'rb').read().decode('utf8', 'replace')
|
||||
if 'soothe2' in m and 'yabridge-host.exe' in c:
|
||||
out.append(pid)
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def scan(pid):
|
||||
# strategy: enumerate every readable chunk; build set of img addresses whose content is 0x18052xxxx
|
||||
# then find heap words equal to any such address
|
||||
mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
|
||||
img_sp = set() # vtbl-slot candidates: addrs in module image holding 0x18052xxxx
|
||||
heap_addrs = [] # (va, chunk) heap chunks
|
||||
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' not in p[1]:
|
||||
continue
|
||||
try:
|
||||
d = os.pread(mem, hi - lo, lo)
|
||||
except Exception:
|
||||
continue
|
||||
if lo < 0x180000000 < hi or (0x180000000 <= lo < 0x183000000):
|
||||
for off in range(0, len(d) - 7, 8):
|
||||
q = struct.unpack_from('<Q', d, off)[0]
|
||||
if 0x180520000 <= q < 0x180555000:
|
||||
img_sp.add(lo + off)
|
||||
else:
|
||||
heap_addrs.append((lo, d))
|
||||
os.close(mem)
|
||||
print('img SP-slot addrs found:', len(img_sp), flush=True)
|
||||
# find heap objects pointing into img_sp
|
||||
found = []
|
||||
for lo, d in heap_addrs:
|
||||
for s in img_sp:
|
||||
t = struct.pack('<Q', s)
|
||||
i = 0
|
||||
while True:
|
||||
i = d.find(t, i)
|
||||
if i < 0:
|
||||
break
|
||||
found.append((s, lo + i))
|
||||
i += 1
|
||||
return img_sp, found
|
||||
|
||||
res = {}
|
||||
t0 = time.time()
|
||||
while time.time() - t0 < 80:
|
||||
for pid in find_hosts():
|
||||
if pid in res:
|
||||
continue
|
||||
img_sp, found = scan(pid)
|
||||
res[pid] = (img_sp, found)
|
||||
print('PID', pid, 'objects pointing to SP-method vtbl:', len(found), flush=True)
|
||||
for s, a in found[:30]:
|
||||
print(f' slotslot {s:#x} object@{a:#x}', flush=True)
|
||||
time.sleep(0.4)
|
||||
proc.kill()
|
||||
print('done', flush=True)
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess, os, glob, sys, time, struct, re
|
||||
|
||||
RPP = sys.argv[1] if len(sys.argv) > 1 else "/home/m/soothe-bt/render_rt.rpp"
|
||||
KEEP = len(sys.argv) > 2 and sys.argv[2] == "keep"
|
||||
|
||||
VPTR = 0x1824abb90 # SpectralProcessor vtbl first entry addr (static). verify at runtime too
|
||||
CTOR = 0x180529610
|
||||
|
||||
proc = subprocess.Popen(
|
||||
['reaper', '-nosplash', '-renderproject', RPP],
|
||||
stdout=open('/tmp/rtdump2.log', 'w'), stderr=subprocess.STDOUT)
|
||||
print('reaper', proc.pid, flush=True)
|
||||
|
||||
host = None
|
||||
deadline = time.time() + 90
|
||||
while time.time() < deadline and not KEEP:
|
||||
found = []
|
||||
for p in glob.glob('/proc/[0-9]*'):
|
||||
try:
|
||||
pid = int(os.path.basename(p))
|
||||
cmd = open(p + '/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||
if 'yabridge-host' not in cmd:
|
||||
continue
|
||||
maps = open(f'/proc/{pid}/maps').read()
|
||||
if 'soothe2' in maps:
|
||||
found.append(pid)
|
||||
except Exception:
|
||||
pass
|
||||
if found:
|
||||
host = found[0]
|
||||
break
|
||||
time.sleep(0.2)
|
||||
print('host', host, flush=True)
|
||||
if not host:
|
||||
if not KEEP:
|
||||
proc.kill()
|
||||
sys.exit('no host')
|
||||
|
||||
# gather ALL readable regions
|
||||
readable = []
|
||||
maps = open(f'/proc/{host}/maps').read()
|
||||
tot = 0
|
||||
for line in maps.splitlines():
|
||||
parts = line.split()
|
||||
lo, hi = (int(x, 16) for x in parts[0].split('-'))
|
||||
if 'r' in parts[1]:
|
||||
readable.append((lo, hi, parts[1]))
|
||||
tot += hi - lo
|
||||
print('readable regions:', len(readable), 'total MB:', tot / 1e6, flush=True)
|
||||
|
||||
mem = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||
pat = {v: struct.pack('<Q', v) for v in (VPTR,)}
|
||||
instances = []
|
||||
scanned = 0
|
||||
for lo, hi, _ in readable:
|
||||
try:
|
||||
d = os.pread(mem, hi - lo, lo)
|
||||
except Exception:
|
||||
continue
|
||||
scanned += len(d)
|
||||
for v, p in pat.items():
|
||||
i = 0
|
||||
while True:
|
||||
i = d.find(p, i)
|
||||
if i < 0:
|
||||
break
|
||||
instances.append((v, lo + i, hi))
|
||||
i += 1
|
||||
os.close(mem)
|
||||
print('scanned bytes MB:', scanned / 1e6, 'instance hits:', len(instances), flush=True)
|
||||
|
||||
if instances:
|
||||
with open('/tmp/instances.txt', 'w') as f:
|
||||
for v, addr, hi in instances:
|
||||
f.write(f'{v:#x} {addr:#x} hi={hi:#x}\n')
|
||||
# dump candidate object memory
|
||||
out = '/tmp/inst_dump.bin'
|
||||
with open(out, 'wb') as f:
|
||||
mem = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||
for v, addr, _ in instances[:16]:
|
||||
base = addr - 0x80
|
||||
f.write(os.pread(mem, 0x2000, base))
|
||||
os.close(mem)
|
||||
print('dumped candidates to', out, flush=True)
|
||||
else:
|
||||
# no instance: dump the module .data section regardless so we can scan for globals
|
||||
print('no instance found', flush=True)
|
||||
|
||||
if not KEEP:
|
||||
proc.kill()
|
||||
print('done', flush=True)
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess, os, glob, sys, time, struct
|
||||
|
||||
RPP = "/home/m/soothe-bt/render_short.rpp"
|
||||
OUT = "/home/m/soothe-bt/out_rt.wav"
|
||||
proc = subprocess.Popen(['reaper', '-nosplash', '-renderproject', RPP],
|
||||
stdout=open('/tmp/rtone.log', 'w'), stderr=subprocess.STDOUT)
|
||||
print('reaper', proc.pid, flush=True)
|
||||
|
||||
def find_hosts():
|
||||
out = []
|
||||
for p in glob.glob('/proc/[0-9]*'):
|
||||
try:
|
||||
pid = int(os.path.basename(p))
|
||||
m = open(f'/proc/{pid}/maps').read()
|
||||
c = open(f'/proc/{pid}/cmdline', 'rb').read().decode('utf8', 'replace')
|
||||
if 'soothe2' in m and 'yabridge-host.exe' in c:
|
||||
out.append(pid)
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
def out_growth():
|
||||
try:
|
||||
return os.path.getsize(OUT)
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
# Phase 1: wait for render to actually produce audio
|
||||
sz0 = out_growth()
|
||||
base_sz = sz0
|
||||
t0 = time.time()
|
||||
hosts = None
|
||||
grew = 0
|
||||
# wait for host
|
||||
while time.time() - t0 < 60:
|
||||
hosts = find_hosts()
|
||||
if hosts:
|
||||
break
|
||||
time.sleep(0.2)
|
||||
print('hosts:', hosts, flush=True)
|
||||
# wait for out file to grow beyond initial (render active)
|
||||
while time.time() - t0 < 120:
|
||||
s = out_growth()
|
||||
if s > base_sz:
|
||||
grew = s
|
||||
break
|
||||
# if reaper exited, stop trying
|
||||
if proc.poll() is not None:
|
||||
print('reaper exited, last out size', s, flush=True)
|
||||
break
|
||||
time.sleep(0.3)
|
||||
print('out grew to', grew, flush=True)
|
||||
if not hosts or grew == 0:
|
||||
proc.kill()
|
||||
sys.exit('no live render')
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
N_POW2 = {2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536}
|
||||
UPS = {2,4,8,16}
|
||||
|
||||
def scan(pid):
|
||||
mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
|
||||
found = []
|
||||
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' not in p[1]:
|
||||
continue
|
||||
if lo < 0x180000000 or lo >= 0x183000000:
|
||||
try:
|
||||
d = os.pread(mem, hi - lo, lo)
|
||||
except Exception:
|
||||
continue
|
||||
if len(d) < 0x1ac + 8:
|
||||
continue
|
||||
for o in range(0, len(d) - (0x1ac + 8), 8):
|
||||
n0 = struct.unpack_from('<I', d, o + 0x19c)[0]
|
||||
n1 = struct.unpack_from('<I', d, o + 0x1a0)[0]
|
||||
u = struct.unpack_from('<I', d, o + 0x1ac)[0]
|
||||
if n0 in N_POW2 and n1 == n0 and u in UPS:
|
||||
ws = struct.unpack_from('<d', d, o + 0x1a4)[0]
|
||||
v0 = struct.unpack_from('<Q', d, o)[0]
|
||||
found.append((lo + o, n0, u, ws, v0))
|
||||
os.close(mem)
|
||||
return found
|
||||
|
||||
for pid in hosts:
|
||||
f = scan(pid)
|
||||
print('PID', pid, 'sig hits:', len(f), flush=True)
|
||||
for x in f[:30]:
|
||||
print(' obj', hex(x[0]), 'N=', x[1], 'ups=', x[2],
|
||||
'win=%.6g' % x[3], 'vptr=%#x' % x[4], flush=True)
|
||||
with open('/tmp/nsig.txt', 'w') as fo:
|
||||
for x in f:
|
||||
fo.write('%d %#x %d %d %.17g %#x\n' % (pid, x[0], x[1], x[2], x[3], x[4]))
|
||||
proc.kill()
|
||||
print('done', flush=True)
|
||||
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess, os, glob, sys, time, struct
|
||||
|
||||
RPP = sys.argv[1] if len(sys.argv) > 1 else "/home/m/soothe-bt/render_long.rpp"
|
||||
|
||||
VCAND = list(range(0x1824abb60, 0x1824abc20, 8)) # wide vtable range
|
||||
CTOR = 0x180529610
|
||||
|
||||
proc = subprocess.Popen(
|
||||
['reaper', '-nosplash', '-renderproject', RPP],
|
||||
stdout=open('/tmp/rtov.log', 'w'), stderr=subprocess.STDOUT)
|
||||
print('reaper', proc.pid, flush=True)
|
||||
|
||||
|
||||
def find_hosts():
|
||||
out = []
|
||||
for p in glob.glob('/proc/[0-9]*'):
|
||||
try:
|
||||
pid = int(os.path.basename(p))
|
||||
maps = open(f'/proc/{pid}/maps').read()
|
||||
if 'soothe2' in maps and 'yabridge-host' in open(f'/proc/{pid}/cmdline','rb').read().decode('utf8','replace'):
|
||||
out.append(pid)
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def scan(pid):
|
||||
mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
|
||||
found = []
|
||||
pats = {v: struct.pack('<Q', v) for v in VCAND}
|
||||
ctor = struct.pack('<Q', CTOR)
|
||||
maps = open(f'/proc/{pid}/maps').read()
|
||||
for line in maps.splitlines():
|
||||
parts = line.split()
|
||||
lo, hi = (int(x, 16) for x in parts[0].split('-'))
|
||||
if 'r' not in parts[1]:
|
||||
continue
|
||||
try:
|
||||
d = os.pread(mem, hi - lo, lo)
|
||||
except Exception:
|
||||
continue
|
||||
for v, p in pats.items():
|
||||
i = 0
|
||||
while True:
|
||||
i = d.find(p, i)
|
||||
if i < 0:
|
||||
break
|
||||
found.append((v, lo + i))
|
||||
i += 1
|
||||
os.close(mem)
|
||||
return found
|
||||
|
||||
hosts = None
|
||||
found = []
|
||||
t0 = time.time()
|
||||
while time.time() - t0 < 60:
|
||||
hosts = find_hosts()
|
||||
if hosts:
|
||||
for h in hosts:
|
||||
f = scan(h)
|
||||
if f:
|
||||
found += [(h, a, b) for a, b in f]
|
||||
if len(found) > 0:
|
||||
print('HITS', host if 'host' in dir() else h, len(found), flush=True)
|
||||
with open('/tmp/inst2.txt', 'w') as fo:
|
||||
for hh, va, a in found:
|
||||
fo.write(f'{hh} {va:#x} {a:#x}\n')
|
||||
if not hosts:
|
||||
proc.kill()
|
||||
sys.exit(0)
|
||||
time.sleep(0.3)
|
||||
print('none', flush=True)
|
||||
proc.kill()
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess, os, glob, sys, time, struct
|
||||
|
||||
RPP = "/home/m/soothe-bt/render_short.rpp"
|
||||
proc = subprocess.Popen(['reaper', '-nosplash', '-renderproject', RPP],
|
||||
stdout=open('/tmp/rtsig.log', 'w'), stderr=subprocess.STDOUT)
|
||||
print('reaper', proc.pid, flush=True)
|
||||
|
||||
|
||||
def find_hosts():
|
||||
out = []
|
||||
for p in glob.glob('/proc/[0-9]*'):
|
||||
try:
|
||||
pid = int(os.path.basename(p))
|
||||
m = open(f'/proc/{pid}/maps').read()
|
||||
c = open(f'/proc/{pid}/cmdline', 'rb').read().decode('utf8', 'replace')
|
||||
if 'soothe2' in m and 'yabridge-host.exe' in c:
|
||||
out.append(pid)
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
N_POW2 = {2:1,4:1,8:1,16:1,32:1,64:1,128:1,256:1,512:1,1024:1,2048:1,4096:1,8192:1,16384:1,32768:1,65536:1}
|
||||
|
||||
def scan(pid):
|
||||
mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
|
||||
found = []
|
||||
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' not in p[1]:
|
||||
continue
|
||||
try:
|
||||
d = os.pread(mem, hi - lo, lo)
|
||||
except Exception:
|
||||
continue
|
||||
if lo < 0x180000000 or lo >= 0x183000000:
|
||||
# heap/stack chunk. scan qword-aligned for N signature
|
||||
for o in range(0, len(d) - (0x1ac + 8), 8):
|
||||
n0 = struct.unpack_from('<I', d, o + 0x19c)
|
||||
n1 = struct.unpack_from('<I', d, o + 0x1a0)
|
||||
u = struct.unpack_from('<I', d, o + 0x1ac)
|
||||
if n0[0] and n1[0] == n0[0] and n0[0] in N_POW2 and u[0] in (2,4,8,16):
|
||||
ws = struct.unpack_from('<d', d, o + 0x1a4)
|
||||
found.append((lo + o, n0[0], u[0], ws[0]))
|
||||
os.close(mem)
|
||||
return found
|
||||
|
||||
done = set()
|
||||
t0 = time.time()
|
||||
while time.time() - t0 < 70:
|
||||
for pid in find_hosts():
|
||||
if pid in done:
|
||||
continue
|
||||
f = scan(pid)
|
||||
done.add(pid)
|
||||
print('PID', pid, 'sig hits:', len(f), flush=True)
|
||||
for x in f[:20]:
|
||||
print(' obj', hex(x[0]), 'N', x[1], 'ups', x[2], 'win', x[3], flush=True)
|
||||
time.sleep(0.3)
|
||||
proc.kill()
|
||||
print('done', flush=True)
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess, os, glob, sys, time, struct
|
||||
|
||||
RPP = "/home/m/soothe-bt/render_short.rpp"
|
||||
OUT = "/home/m/soothe-bt/out_rt.wav"
|
||||
CAND = [0x2370040, 0x1580040]
|
||||
proc = subprocess.Popen(['reaper', '-nosplash', '-renderproject', RPP],
|
||||
stdout=open('/tmp/rtver.log', 'w'), stderr=subprocess.STDOUT)
|
||||
print('reaper', proc.pid, flush=True)
|
||||
|
||||
def find_hosts():
|
||||
out = []
|
||||
for p in glob.glob('/proc/[0-9]*'):
|
||||
try:
|
||||
pid = int(os.path.basename(p))
|
||||
m = open(f'/proc/{pid}/maps').read()
|
||||
c = open(f'/proc/{pid}/cmdline', 'rb').read().decode('utf8', 'replace')
|
||||
if 'soothe2' in m and 'yabridge-host.exe' in c:
|
||||
out.append(pid)
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
def out_growth():
|
||||
try:
|
||||
return os.path.getsize(OUT)
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
t0 = time.time()
|
||||
hosts = None
|
||||
base_sz = out_growth()
|
||||
while time.time() - t0 < 60:
|
||||
hosts = find_hosts()
|
||||
if hosts:
|
||||
break
|
||||
time.sleep(0.2)
|
||||
print('hosts:', hosts, flush=True)
|
||||
grew = 0
|
||||
while time.time() - t0 < 120:
|
||||
s = out_growth()
|
||||
if s > base_sz:
|
||||
grew = s; break
|
||||
if proc.poll() is not None:
|
||||
print('reaper exited early', flush=True); break
|
||||
time.sleep(0.3)
|
||||
print('out:', grew, flush=True)
|
||||
if not hosts or grew == 0:
|
||||
proc.kill(); sys.exit('fail')
|
||||
time.sleep(0.5)
|
||||
|
||||
for pid in hosts:
|
||||
mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
|
||||
for a in CAND:
|
||||
try:
|
||||
d = os.pread(mem, 0x300, a)
|
||||
except Exception as e:
|
||||
print(pid, hex(a), 'err', e, flush=True); continue
|
||||
# ?possibly the object base is NOT a; our signature matched offset at +0x19c.
|
||||
# Re-scan around a for the exact int field to find true object base.
|
||||
# fields: N at base+0x19c, N at +0x1a0, float win at +0x1a4, int ups at +0x1ac
|
||||
# find qword-aligned true base by searching +0x1ac==4 in window
|
||||
f4 = struct.unpack('<%dI' % (len(d) // 4), d)
|
||||
bases = []
|
||||
for o in range(0, len(f4) - (0x1ac // 4 + 1)):
|
||||
if f4[o + 0x1ac // 4] == 4 and f4[o + 0x19c // 4] in (2048, 4096) \
|
||||
and f4[o + 0x1a0 // 4] == f4[o + 0x19c // 4]:
|
||||
bases.append(o * 4)
|
||||
print(pid, hex(a), 'true-base candidates:', [hex(a + b) for b in bases[:6]], flush=True)
|
||||
for b in bases[:1]:
|
||||
o = b
|
||||
vptr = struct.unpack_from('<Q', d, o)[0]
|
||||
n = struct.unpack_from('<I', d, o + 0x19c)[0]
|
||||
na = struct.unpack_from('<I', d, o + 0x1a0)[0]
|
||||
win = struct.unpack_from('<f', d, o + 0x1a4)[0]
|
||||
ups = struct.unpack_from('<I', d, o + 0x1ac)[0]
|
||||
print(' base', hex(a + o), 'vptr', hex(vptr), 'N', n, 'N1', na,
|
||||
'win', win, 'ups', ups, flush=True)
|
||||
# dump vtbl head
|
||||
try:
|
||||
vd = os.pread(mem, 0x80, vptr)
|
||||
qs = struct.unpack('<10Q', vd)
|
||||
print(' vtbl[0..9]:', ' '.join('%#x' % q for q in qs), flush=True)
|
||||
except Exception as e:
|
||||
print(' vtbl err', e, flush=True)
|
||||
os.close(mem)
|
||||
proc.kill()
|
||||
print('done', flush=True)
|
||||
Reference in New Issue
Block a user