24mm12: detector cascade FOUND = vtable stage vt+0x28 = 180529c60 (0x281 bytes, mixes bands@678 + prev track, x0.5, vec6f8 helpers); vtable pipeline map; fn529fe0 only builds kernel from ready tracks
This commit is contained in:
@@ -4236,3 +4236,54 @@ lvl_raw → track_i (буфер [rsp+0x138][band]) и пост-обработк
|
|||||||
2. Найти консюмера track/FIR после fn529fe0 (FFT-conv движок) — как
|
2. Найти консюмера track/FIR после fn529fe0 (FFT-conv движок) — как
|
||||||
track_i превращается в применённую маску аудио.
|
track_i превращается в применённую маску аудио.
|
||||||
3. C++ порт RT_CASCADE=1 (Этап C) — цепь до df0 уже можно транскрибировать.
|
3. C++ порт RT_CASCADE=1 (Этап C) — цепь до df0 уже можно транскрибировать.
|
||||||
|
|
||||||
|
## ============ 24mm12: НАЙДЕН ДЕТЕКТОРНЫЙ КАСКАД = СТАДИЯ vt+0x28 = 180529c60; КАРТА VTABLE ПАЙПЛАЙНА ============
|
||||||
|
|
||||||
|
### Оркестратор (0x1805300f0..0x1805305e7)
|
||||||
|
fn529fe0 вызывается ВИРТУАЛЬНО: `call [ctx_vtable+0x30]` (сайт 530371),
|
||||||
|
аргументы (rcx=ctx, rdx=ТАБЛИЦА TRACK = ctx+0x380, r8d=4096=[ctx+0x1a8],
|
||||||
|
r9d=nbands=[ctx+0x30]). До него в том же оркестраторе — серия виртуальных
|
||||||
|
вызовов других стадий. Полоса-цикл после fn: cmp [rsi+0x30],r13;
|
||||||
|
r15=ctx+0x360, r14=ctx+0x2e0 — ещё таблицы-указатели в малых оффсетах ctx.
|
||||||
|
|
||||||
|
### Карта vtable ctx (live, comb_b1234)
|
||||||
|
```
|
||||||
|
+0x08 0x18052cfb0 +0x10 0x180529550 +0x18 0x1804714b0 (чужой модуль?)
|
||||||
|
+0x20 0x18052b940 +0x28 0x180529c60 ← ДЕТЕКТОР +0x30 0x180529fe0 (fn)
|
||||||
|
+0x38 0x180529ef0 (prep) +0x40 0x18052bbf0
|
||||||
|
+0xe8 0x1804731c0 (чужой модуль?) +0x218 0x180481210
|
||||||
|
```
|
||||||
|
Трасса кадра: [e8,e8]? → 38 → 28(×nbands) → 30(fn). Писец track — ТОЛЬКО
|
||||||
|
vt+0x28 (430/430 изменений md5).
|
||||||
|
|
||||||
|
### 180529c60 — детекторный каскад, ВСЯ функция 0x281 байт
|
||||||
|
```
|
||||||
|
lock bts [ctx+0x2404dc] ; тот же лок что в fn
|
||||||
|
r9 = rdx (СТАРЫЙ track всей таблицы? базовый буфер)
|
||||||
|
r12 = band*2; rcx = [ctx + r12*8 + 0x540678] ; bands_curve полосы
|
||||||
|
n2 = ([ctx+0x64]±)/2+1
|
||||||
|
call 0x1805355d0(bands, rdx, n2) ; ?? первичное смешение
|
||||||
|
loop по [ctx+0x1b0]:
|
||||||
|
rbp = [ctx+r12*8+0x540678]
|
||||||
|
call 0x180530080(rbp, rbp+4, n2-1) ; ??
|
||||||
|
call 0x18052d920(rbp, xmm1=0.5f, n2-1) ; масштаб ×0.5 !
|
||||||
|
r15 = [ctx+0x5406f8] ; vec6f8
|
||||||
|
call 0x1800020f0 / 0x180001850(rsi=rbp+4, rdx=rbp, r8=r15, r9=n2-1)
|
||||||
|
call 0x180001a00 / ...(rcx=r15, rdx=rsi, xmm=0.5f/0.5d)
|
||||||
|
```
|
||||||
|
То есть «шаги 9–17» из BLOCKMAP 24mm5 на самом деле живут ЗДЕСЬ (+хелперы),
|
||||||
|
а НЕ в fn529fe0: fn529fe0 получает ГОТОВЫЕ track-буферы (arg2) и строит
|
||||||
|
из них ядро (df0: T⊗F), не вычисляя детектор!
|
||||||
|
|
||||||
|
### Статус Этапа B
|
||||||
|
Осталось декодировать: хелперы 5355d0 / 530080 / 20f0 / 1850 / 1a00 /
|
||||||
|
52d920-семантику и роль vec6f8@6f8 — десятки инструкций, вся рекуррентия
|
||||||
|
track_{t+1} замкнётся офлайн. Затем γ=1.760561 выводится аналитически.
|
||||||
|
|
||||||
|
### Инструменты раунда
|
||||||
|
scripts/wine_stage_trace.py (vtable-stage трассировщик c md5-атрибуцией
|
||||||
|
записей), scripts/wine_ptrace_trace.py (+TRACKSAVE/DIV/DC40/EXPVAR/FN,
|
||||||
|
mapped-фильтр, строгая валидация ctx по trk==exp(scr)).
|
||||||
|
Грабли: адреса-константы в патчах скрипта молча не применялись при чужом
|
||||||
|
отступе — проверять installed-list; DC40=0x1800dc40 (не 0x18000dc40);
|
||||||
|
DIV/DC40/EXPVAR/FN могут быть вне маппинга процесса (пропускать).
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ BP_EXP = 0x1803831c0
|
|||||||
BP_DF0 = 0x18000b3c0
|
BP_DF0 = 0x18000b3c0
|
||||||
BP_COPY = 0x1800136e0
|
BP_COPY = 0x1800136e0
|
||||||
BP_DF0RET = 0x18052b898
|
BP_DF0RET = 0x18052b898
|
||||||
|
BP_TRACKSAVE = 0x18052b574
|
||||||
|
BP_DIV = 0x1803a06a0
|
||||||
|
BP_DC40 = 0x1800dc40
|
||||||
|
BP_EXPVAR = 0x1802dc0e0
|
||||||
|
BP_FN = 0x180529fe0
|
||||||
CTX_SLOTS = {'scr': 0x540628, 'trk': 0x540688, 'cur': 0x540678,
|
CTX_SLOTS = {'scr': 0x540628, 'trk': 0x540688, 'cur': 0x540678,
|
||||||
'fir_ptr': 0x540668}
|
'fir_ptr': 0x540668}
|
||||||
|
|
||||||
@@ -256,18 +261,40 @@ def main():
|
|||||||
|
|
||||||
# Фаза 3: int3 и запуск
|
# Фаза 3: int3 и запуск
|
||||||
bps = {}
|
bps = {}
|
||||||
|
# проверка маппенности по /proc/pid/maps
|
||||||
|
maps_txt = open(f'/proc/{host}/maps').read()
|
||||||
|
|
||||||
|
def mapped(a):
|
||||||
|
for ln in maps_txt.splitlines():
|
||||||
|
rng = ln.split()[0]
|
||||||
|
lo, hi = (int(x, 16) for x in rng.split('-'))
|
||||||
|
if lo <= a < hi:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
for name, addr in (('COPY', BP_COPY), ('EXP', BP_EXP), ('DF0', BP_DF0),
|
for name, addr in (('COPY', BP_COPY), ('EXP', BP_EXP), ('DF0', BP_DF0),
|
||||||
('DF0RET', BP_DF0RET)):
|
('DF0RET', BP_DF0RET), ('TRACKSAVE', BP_TRACKSAVE),
|
||||||
|
('DIV', BP_DIV), ('DC40', BP_DC40),
|
||||||
|
('EXPVAR', BP_EXPVAR), ('FN', BP_FN)):
|
||||||
|
if not mapped(addr):
|
||||||
|
print('!! %s@%#x не смапплен — пропуск' % (nm_ := name, addr), flush=True)
|
||||||
|
continue
|
||||||
orig = peek(host, addr)
|
orig = peek(host, addr)
|
||||||
poke(host, addr, (orig & ~0xFF) | 0xCC)
|
poke(host, addr, (orig & ~0xFF) | 0xCC)
|
||||||
bps[addr] = (name, orig & 0xFF)
|
bps[addr] = (name, orig & 0xFF)
|
||||||
print('int3 installed:', {hex(a): n for a, (n, _) in bps.items()}, flush=True)
|
print('int3 installed:', {hex(a): n for a, (n, _) in bps.items()}, flush=True)
|
||||||
|
for addr, (nm, _) in bps.items():
|
||||||
|
rb = peek(host, addr) & 0xFF
|
||||||
|
if rb != 0xCC:
|
||||||
|
print('!! %s@%#x НЕ 0xCC: %#02x' % (nm, addr, rb), flush=True)
|
||||||
for tid in attached:
|
for tid in attached:
|
||||||
pt(PTRACE_CONT, tid, 0, 0)
|
pt(PTRACE_CONT, tid, 0, 0)
|
||||||
|
|
||||||
samples = []
|
samples = []
|
||||||
hits = {'COPY': 0, 'EXP': 0, 'DF0': 0, 'DF0RET': 0}
|
hits = {'COPY': 0, 'EXP': 0, 'DF0': 0, 'DF0RET': 0, 'TRACKSAVE': 0,
|
||||||
|
'DIV': 0, 'DC40': 0, 'EXPVAR': 0, 'FN': 0}
|
||||||
track_by_tid = {}
|
track_by_tid = {}
|
||||||
|
track_dumps = []
|
||||||
t_start = time.time()
|
t_start = time.time()
|
||||||
|
|
||||||
def snapshot_slots(rec):
|
def snapshot_slots(rec):
|
||||||
@@ -309,6 +336,70 @@ def main():
|
|||||||
pt(PTRACE_CONT, pid, 0, 0)
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
continue
|
continue
|
||||||
kind, obyte = info
|
kind, obyte = info
|
||||||
|
if kind == 'TRACKSAVE':
|
||||||
|
# rax = track-ptr текущей полосы, r12 = индекс полосы,
|
||||||
|
# [rsp+0x138] = база таблицы указателей (arg2 fn529fe0)
|
||||||
|
tbl = rd_q(regs.rsp + 0x138) if regs.rsp else 0
|
||||||
|
rec_t = {'kind': 'TRACKSAVE', 'tid': pid, 'band': regs.r12,
|
||||||
|
'track_ptr': regs.rax, 'tbl': tbl,
|
||||||
|
't': round(time.time()-t_start, 4)}
|
||||||
|
if len(track_dumps) < 48:
|
||||||
|
try:
|
||||||
|
rec_t['tbl_entries'] = [rd_q(tbl+8*i) for i in range(16)]
|
||||||
|
rec_t['trk_curve'] = rd_f32(regs.rax, 2049*2)
|
||||||
|
except OSError as e:
|
||||||
|
rec_t['err'] = str(e)
|
||||||
|
track_dumps.append(rec_t)
|
||||||
|
samples.append(rec_t)
|
||||||
|
hits['TRACKSAVE'] += 1
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | obyte)
|
||||||
|
regs.rip = site
|
||||||
|
setregs(pid, regs)
|
||||||
|
pt(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
os.waitpid(pid, __WALL)
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | 0xCC)
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
|
if kind == 'FN':
|
||||||
|
ra = rd_q(regs.rsp)
|
||||||
|
rec_f = {'kind':'FN','tid':pid,
|
||||||
|
'rcx':regs.rcx,'rdx':regs.rdx,'r8':regs.r8,'r9':regs.r9,
|
||||||
|
'ret':ra,'t':round(time.time()-t_start,4)}
|
||||||
|
samples.append(rec_f); hits['FN'] += 1
|
||||||
|
if hits['FN'] <= 3:
|
||||||
|
print('FN: rcx=%#x rdx=%#x r8=%#x r9=%#x ret=%#x'%(
|
||||||
|
regs.rcx,regs.rdx,regs.r8,regs.r9,ra), flush=True)
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | obyte)
|
||||||
|
regs.rip = site
|
||||||
|
setregs(pid, regs)
|
||||||
|
pt(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
os.waitpid(pid, __WALL)
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | 0xCC)
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
|
if kind in ('DIV','DC40','EXPVAR'):
|
||||||
|
rec_a = {'kind': kind, 'tid': pid,
|
||||||
|
't': round(time.time()-t_start, 4),
|
||||||
|
'rcx': regs.rcx, 'rdx': regs.rdx,
|
||||||
|
'r8': regs.r8, 'r9': regs.r9}
|
||||||
|
try:
|
||||||
|
for nm, p, cnt in (('a', regs.rcx, 2050),
|
||||||
|
('b', regs.rdx, 2050),
|
||||||
|
('c', regs.r8, 2050)):
|
||||||
|
if p > 0x10000:
|
||||||
|
rec_a[nm] = rd_f32(p, cnt)
|
||||||
|
except OSError as e:
|
||||||
|
rec_a['err'] = str(e)
|
||||||
|
samples.append(rec_a)
|
||||||
|
hits[kind] += 1
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | obyte)
|
||||||
|
regs.rip = site
|
||||||
|
setregs(pid, regs)
|
||||||
|
pt(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
os.waitpid(pid, __WALL)
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | 0xCC)
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
if kind == 'DF0':
|
if kind == 'DF0':
|
||||||
track_by_tid[pid] = regs.rdx
|
track_by_tid[pid] = regs.rdx
|
||||||
if kind == 'DF0RET':
|
if kind == 'DF0RET':
|
||||||
@@ -387,6 +478,46 @@ def main():
|
|||||||
flush=True)
|
flush=True)
|
||||||
if good:
|
if good:
|
||||||
ctx = good
|
ctx = good
|
||||||
|
if kind == 'FN':
|
||||||
|
ra = rd_q(regs.rsp)
|
||||||
|
rec_f = {'kind':'FN','tid':pid,
|
||||||
|
'rcx':regs.rcx,'rdx':regs.rdx,'r8':regs.r8,'r9':regs.r9,
|
||||||
|
'ret':ra,'t':round(time.time()-t_start,4)}
|
||||||
|
samples.append(rec_f); hits['FN'] += 1
|
||||||
|
if hits['FN'] <= 3:
|
||||||
|
print('FN: rcx=%#x rdx=%#x r8=%#x r9=%#x ret=%#x'%(
|
||||||
|
regs.rcx,regs.rdx,regs.r8,regs.r9,ra), flush=True)
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | obyte)
|
||||||
|
regs.rip = site
|
||||||
|
setregs(pid, regs)
|
||||||
|
pt(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
os.waitpid(pid, __WALL)
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | 0xCC)
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
|
if kind in ('DIV','DC40','EXPVAR'):
|
||||||
|
rec_a = {'kind': kind, 'tid': pid,
|
||||||
|
't': round(time.time()-t_start, 4),
|
||||||
|
'rcx': regs.rcx, 'rdx': regs.rdx,
|
||||||
|
'r8': regs.r8, 'r9': regs.r9}
|
||||||
|
try:
|
||||||
|
for nm, p, cnt in (('a', regs.rcx, 2050),
|
||||||
|
('b', regs.rdx, 2050),
|
||||||
|
('c', regs.r8, 2050)):
|
||||||
|
if p > 0x10000:
|
||||||
|
rec_a[nm] = rd_f32(p, cnt)
|
||||||
|
except OSError as e:
|
||||||
|
rec_a['err'] = str(e)
|
||||||
|
samples.append(rec_a)
|
||||||
|
hits[kind] += 1
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | obyte)
|
||||||
|
regs.rip = site
|
||||||
|
setregs(pid, regs)
|
||||||
|
pt(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
os.waitpid(pid, __WALL)
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | 0xCC)
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
if kind == 'DF0':
|
if kind == 'DF0':
|
||||||
track_by_tid[pid] = regs.rdx
|
track_by_tid[pid] = regs.rdx
|
||||||
rec = {'kind': kind, 'tid': pid,
|
rec = {'kind': kind, 'tid': pid,
|
||||||
|
|||||||
@@ -0,0 +1,244 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""wine_stage_trace.py — трассировка СТАДИЙ пайплайна через vtable ctx.
|
||||||
|
|
||||||
|
На входе fn529fe0: читает vtable=[ctx], ставит int3 на таргеты слотов
|
||||||
|
{8,0x18,0x20,0x28,0x30,0x48,0xe8,0x218,0x220,0x228,0x230}, снапшотит
|
||||||
|
track-буферы (таблица @arg2, count=r9). На каждом хите стадии: md5
|
||||||
|
track-буферов + аргументы. Разница md5 между стадиями = кто пишет track.
|
||||||
|
"""
|
||||||
|
import ctypes
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from wine_ptrace_trace import ( # noqa
|
||||||
|
pt, getregs, setregs, peek, poke, find_host, find_ctx,
|
||||||
|
PTRACE_ATTACH, PTRACE_DETACH, PTRACE_CONT, PTRACE_SINGLESTEP,
|
||||||
|
PTRACE_SETOPTIONS, PTRACE_O_TRACECLONE, __WALL)
|
||||||
|
|
||||||
|
BP_FN = 0x180529fe0
|
||||||
|
SLOTS = [0x8, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48,
|
||||||
|
0xe8, 0x218, 0x220, 0x228, 0x230]
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
rpp = sys.argv[1] if len(sys.argv) > 1 else '/home/m/soothe-bt/comb_b1234.rpp'
|
||||||
|
n_frames = int(sys.argv[2]) if len(sys.argv) > 2 else 6
|
||||||
|
outdir = sys.argv[3] if len(sys.argv) > 3 else '/tmp/opencode/winetrace_casc'
|
||||||
|
os.makedirs(outdir, exist_ok=True)
|
||||||
|
|
||||||
|
wav = None
|
||||||
|
for ln in open(rpp, errors='replace'):
|
||||||
|
if 'RENDER_FILE' in ln and '"' in ln:
|
||||||
|
wav = ln.split('"')[1]
|
||||||
|
break
|
||||||
|
|
||||||
|
subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; sleep 1",
|
||||||
|
shell=True)
|
||||||
|
proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', rpp],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
t0 = time.time()
|
||||||
|
host = None
|
||||||
|
while time.time() - t0 < 25:
|
||||||
|
host = find_host()
|
||||||
|
if host:
|
||||||
|
break
|
||||||
|
time.sleep(0.001)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
fd = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||||
|
ctx = None
|
||||||
|
while ctx is None and time.time() - t0 < 25:
|
||||||
|
try:
|
||||||
|
ctx = find_ctx(fd, host)
|
||||||
|
except (ProcessLookupError, OSError):
|
||||||
|
return 1
|
||||||
|
if not ctx:
|
||||||
|
time.sleep(0.002)
|
||||||
|
print('host %d ctx %#x (+%.2fs)' % (host, ctx, time.time()-t0), flush=True)
|
||||||
|
|
||||||
|
def rd(a, n):
|
||||||
|
return os.pread(fd, n, a)
|
||||||
|
|
||||||
|
def rd_f32(a, n):
|
||||||
|
return np.frombuffer(rd(a, 4*n), dtype='<f4').astype(np.float64)
|
||||||
|
|
||||||
|
def rd_q(a):
|
||||||
|
return struct.unpack('<Q', rd(a, 8))[0]
|
||||||
|
|
||||||
|
maps_txt = open(f'/proc/{host}/maps').read()
|
||||||
|
|
||||||
|
def mapped(a):
|
||||||
|
for ln in maps_txt.splitlines():
|
||||||
|
rng = ln.split()[0]
|
||||||
|
lo, hi = (int(x, 16) for x in rng.split('-'))
|
||||||
|
if lo <= a < hi:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
# attach
|
||||||
|
tids = [int(t) for t in os.listdir(f'/proc/{host}/task')]
|
||||||
|
attached = []
|
||||||
|
for tid in tids:
|
||||||
|
try:
|
||||||
|
if pt(PTRACE_ATTACH, tid) == -1 and ctypes.get_errno():
|
||||||
|
raise OSError(ctypes.get_errno())
|
||||||
|
os.waitpid(tid, __WALL)
|
||||||
|
pt(PTRACE_SETOPTIONS, tid, 0, PTRACE_O_TRACECLONE)
|
||||||
|
attached.append(tid)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
print('attached %d' % len(attached), flush=True)
|
||||||
|
|
||||||
|
# vtable + стадии
|
||||||
|
vt = rd_q(ctx)
|
||||||
|
stage_targets = {}
|
||||||
|
for s in SLOTS:
|
||||||
|
tgt = rd_q(vt + s)
|
||||||
|
if mapped(tgt) and tgt not in stage_targets.values():
|
||||||
|
stage_targets[s] = tgt
|
||||||
|
inv = {v: ('vt+%#x' % k) for k, v in stage_targets.items()}
|
||||||
|
print('стадии:', {hex(k): hex(v) for k, v in stage_targets.items()}, flush=True)
|
||||||
|
|
||||||
|
bps = {}
|
||||||
|
for slot, tgt in stage_targets.items():
|
||||||
|
orig = peek(host, tgt)
|
||||||
|
poke(host, tgt, (orig & ~0xFF) | 0xCC)
|
||||||
|
bps[tgt] = (('vt%#x' % slot), orig & 0xFF)
|
||||||
|
orig_fn = peek(host, BP_FN)
|
||||||
|
poke(host, BP_FN, (orig_fn & ~0xFF) | 0xCC)
|
||||||
|
bps[BP_FN] = ('FN', orig_fn & 0xFF)
|
||||||
|
|
||||||
|
for tid in attached:
|
||||||
|
pt(PTRACE_CONT, tid, 0, 0)
|
||||||
|
|
||||||
|
samples = []
|
||||||
|
frames_done = 0
|
||||||
|
cur_frame = None
|
||||||
|
t_start = time.time()
|
||||||
|
|
||||||
|
def track_snapshot(table, nbands):
|
||||||
|
out = {}
|
||||||
|
for i in range(nbands):
|
||||||
|
p = rd_q(table + 8*i)
|
||||||
|
if p > 0x10000:
|
||||||
|
out[i] = hashlib.md5(rd(p, 4098*4)).hexdigest()
|
||||||
|
return out
|
||||||
|
|
||||||
|
try:
|
||||||
|
while frames_done < n_frames and time.time() - t_start < 240:
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(-1, __WALL | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if (pid, status) == (0, 0):
|
||||||
|
time.sleep(0.0005)
|
||||||
|
continue
|
||||||
|
if not os.WIFSTOPPED(status):
|
||||||
|
if pid in attached:
|
||||||
|
attached.remove(pid)
|
||||||
|
continue
|
||||||
|
if os.WSTOPSIG(status) != signal.SIGTRAP:
|
||||||
|
pt(PTRACE_CONT, pid, 0, sig if False else 0)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
regs = getregs(pid)
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
site = regs.rip - 1
|
||||||
|
info = bps.get(site)
|
||||||
|
if info is None:
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
|
kind, obyte = info
|
||||||
|
|
||||||
|
def restore_and_go():
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | obyte)
|
||||||
|
regs.rip = site
|
||||||
|
setregs(pid, regs)
|
||||||
|
pt(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
os.waitpid(pid, __WALL)
|
||||||
|
poke(pid, site, (peek(pid, site) & ~0xFF) | 0xCC)
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
|
||||||
|
if kind == 'FN':
|
||||||
|
table = regs.rdx
|
||||||
|
nb = regs.r9 & 0xFFFFFFFF
|
||||||
|
cur_frame = {'t': round(time.time()-t_start, 4),
|
||||||
|
'ctx': regs.rcx, 'table': table, 'nbands': nb,
|
||||||
|
'md5_before': track_snapshot(table, nb),
|
||||||
|
'stages': []}
|
||||||
|
rec = dict(kind='FN', **{k: v for k, v in cur_frame.items()
|
||||||
|
if k != 'md5_before'})
|
||||||
|
samples.append(rec)
|
||||||
|
else:
|
||||||
|
if cur_frame is not None:
|
||||||
|
ent = {'stage': kind, 'site': hex(site),
|
||||||
|
'rcx': regs.rcx, 'rdx': regs.rdx,
|
||||||
|
'r8': regs.r8, 'r9': regs.r9,
|
||||||
|
'md5_after': track_snapshot(cur_frame['table'],
|
||||||
|
cur_frame['nbands'])}
|
||||||
|
cur_frame['stages'].append(ent)
|
||||||
|
if kind.startswith('vt') and frames_done < 2:
|
||||||
|
args = {}
|
||||||
|
for nm, p in (('rcx', regs.rcx), ('rdx', regs.rdx),
|
||||||
|
('r8', regs.r8)):
|
||||||
|
if p > 0x10000:
|
||||||
|
try:
|
||||||
|
args[nm] = rd_f32(p, 2050)[:64].tolist()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
samples.append({'kind': 'ARG:' + kind, 'site': hex(site),
|
||||||
|
'args64': str(args)[:400]})
|
||||||
|
if kind == 'vt+0x30':
|
||||||
|
# fn529fe0 завершился: финальный md5
|
||||||
|
if cur_frame is not None:
|
||||||
|
cur_frame['md5_after_fn'] = track_snapshot(
|
||||||
|
cur_frame['table'], cur_frame['nbands'])
|
||||||
|
frames_done += 1
|
||||||
|
samples.append({'kind': 'FRAME_END',
|
||||||
|
'frame': cur_frame})
|
||||||
|
cur_frame = None
|
||||||
|
|
||||||
|
restore_and_go()
|
||||||
|
finally:
|
||||||
|
for addr, (nm, obyte) in bps.items():
|
||||||
|
try:
|
||||||
|
poke(host, addr, (peek(host, addr) & ~0xFF) | obyte)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
for tid in list(attached):
|
||||||
|
try:
|
||||||
|
pt(PTRACE_DETACH, tid, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
with open(os.path.join(outdir, 'stage_samples.pkl'), 'wb') as f:
|
||||||
|
pickle.dump(samples, f)
|
||||||
|
fr = [s for s in samples if s['kind'] == 'FRAME_END']
|
||||||
|
print('кадров собрано:', len(fr), flush=True)
|
||||||
|
for f_ in fr[:3]:
|
||||||
|
fr_ = f_['frame']
|
||||||
|
print('--- кадр t=%.2f bands=%d' % (fr_['t'], fr_['nbands']))
|
||||||
|
prev = fr_['md5_before']
|
||||||
|
for st in fr_['stages']:
|
||||||
|
ch = '' if st['md5_after'] == prev else ' <<< TRACK ИЗМЕНИЛСЯ'
|
||||||
|
print(' %-8s rcx=%#x rdx=%#x%s' % (st['stage'], st['rcx'],
|
||||||
|
st['rdx'], ch))
|
||||||
|
prev = st['md5_after']
|
||||||
|
print(' после fn:', fr_.get('md5_after_fn'))
|
||||||
|
print('reaper_rc=%s' % proc.poll(), flush=True)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user