1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
#!/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))
|