#!/usr/bin/env python3 # Analyze the live r673 guest RAM: locate every reference to the validated ring # PA (0x12a783000) including flag-bit variants, and characterize the record # arrays around each hit. Also dump the two pages named in the handoff. import numpy as np, struct, sys, time RAM="/dev/shm/vgpu-guest-ram" RING=0x12a783000 MASK=~0xfff & ((1<<64)-1) def gpa2off(pa): if pa < 0x80000000: return pa if pa < 0x180000000: return 0x80000000+(pa-0x100000000) return None def off2gpa(off): return off if off < 0x80000000 else 0x100000000+(off-0x80000000) img=open(RAM,"rb") import os fsz=os.path.getsize(RAM) print("ram size 0x%x" % fsz) t0=time.time() hits=[] CH=64<<20 for pos in range(0,fsz,CH): img.seek(pos); d=img.read(min(CH,fsz-pos)) n=len(d)//8 a=np.frombuffer(d[:n*8],dtype=np.uint64) idx=np.nonzero((a & np.uint64(MASK))==np.uint64(RING))[0] for i in idx: off=pos+int(i)*8 val=int(a[i]) hits.append((off,val)) print("hits=%d scan=%.1fs" % (len(hits), time.time()-t0)) for off,val in hits: print(" off=0x%x gpa=0x%x val=0x%x (low=0x%x)" % (off,off2gpa(off),val,val&0xfff)) # characterize each hit page def rd(pa,n): off=gpa2off(pa) if off is None or off+n>fsz: return None img.seek(off); return img.read(n) def plausible_pa(v): return 0x100000000 <= (v & MASK) < 0x180000000 def ptrish(v): return (0xffff800000000000 <= v < 0xffffffffffffffff) for off,val in hits: gpa=off2gpa(off) page=gpa & MASK rec_off=gpa-page d=rd(page,0x1000) print("=== hit gpa=0x%x page=0x%x rec_off=0x%x ===" % (gpa,page,rec_off)) for stride in (0x20,0x40,0x60,0x30,0x50,0x28,0x48): # record start such that the hit qword falls inside the record for rel in range(0,stride,8): start=rec_off-rel if start<0: continue # walk records; count plausible PA fields anywhere in each record n_pa=0; n_ptr=0; n_1000=0 for o in range(start,0x1000-stride,stride): rec=d[o:o+stride] qs=struct.unpack_from("<%dQ"%(stride//8),rec,0) if any(plausible_pa(q) for q in qs): n_pa+=1 if any(ptrish(q) for q in qs): n_ptr+=1 if 0x1000 in qs: n_1000+=1 if n_pa>=8: print(" stride=0x%x start=0x%x: PA-records=%d ptr-records=%d 0x1000=%d" % (stride,start,n_pa,n_ptr,n_1000)) # dump the first 0x100 bytes of the page and around the hit print(" page head: %s" % d[:0x60].hex()) s=max(0,rec_off-0x40); e=min(0x1000,rec_off+0x80) print(" around hit [0x%x..0x%x]: %s" % (s,e,d[s:e].hex())) # dump the two named pages for pa in (0x101499000,0x127cf2000): d=rd(pa,0x1000) print("=== page 0x%x ===" % pa) if d is None: print(" unreadable"); continue for o in range(0,0x120,0x20): qs=struct.unpack_from("<4Q",d,o) print(" +0x%03x: %016x %016x %016x %016x" % ((o,)+qs))