#!/usr/bin/perl -w # # Page Table Dump of running process in qemu-x86_64 # # Run on a stopped VM for reliable output # use strict; use IO::Socket::UNIX; die "Use: $0 unix-monitor [cr3]" unless @ARGV; my $mon = IO::Socket::UNIX->new(Type => SOCK_STREAM, Peer => shift); my $cr3; if (@ARGV) { $cr3 = shift; print "Looking up page tables as if CR3=$cr3\n"; } else { print $mon "info registers\n"; while (<$mon>) { m/^RIP=([0-9a-f]{16}) / and print "RIP=$1 "; m/ CR3=([0-9a-f]{16}) / and $cr3 = $1; last if / XMM15=/; } die unless defined $cr3; $cr3 = hex $cr3; printf "CR3=%016x\n", $cr3; } sub xp { my $ma = shift; my $res = sprintf "%016x: 0x([0-9a-f]+)", $ma; print $mon "xp $ma\n"; while (<$mon>) { return hex $1 if /$res/; } die; } my $pml4 = $cr3 & 0xFFFFFFFFFFFFF000; my($vstart, $pstart, $vcurr, $pcurr, $fcurr) = (1,0,1,0,0); sub txpage { my($vaddr, $paddr, $psiz, $flags) = @_; if ($vaddr == $vcurr && $paddr == $pcurr && $flags == $fcurr) { $vcurr += $psiz; $pcurr += $psiz; return; } if ($vstart != 1) { my $len = $vcurr - $vstart; printf " Translation: %16x -> %12x %8x f=%012b\n", $vstart, $pstart, $len, $fcurr; } $vstart = $vcurr = $vaddr; $pstart = $pcurr = $paddr; $fcurr = $flags; $vcurr = $vaddr + $psiz; $pcurr = $paddr + $psiz; } for my $pml4i (0..0x1FF) { my $pml4e1 = xp $pml4 + $pml4i * 0x8; my $pml4e2 = xp $pml4 + $pml4i * 0x8 + 4; next unless $pml4e1 & 1; my $pdpt = (($pml4e2 << 32) | $pml4e1) & 0xFFFFFFFFF000; printf "Using PML4 entry %03x %12x f=%012b\n", $pml4i << 3, $pdpt, $pml4e1 & 0xFFF; for my $pdpti (0..0x1FF) { my $pdpte1 = xp $pdpt + $pdpti * 0x8; my $pdpte2 = xp $pdpt + $pdpti * 0x8 + 4; next unless $pdpte1 & 1; if ($pdpte1 & 0x80) { print "TODO: 1GB page\n"; next; } my $pd = (($pdpte2 << 32) | $pdpte1) & 0xFFFFFFFFF000; printf " Using PDPT entry %03x %12x f=%012b\n", $pdpti << 2, $pd, $pdpte1 & 0xFFF; for my $pdi (0..0x1FF) { my $pde1 = xp $pd + $pdi * 0x8; my $pde2 = xp $pd + $pdi * 0x8 + 4; next unless $pde1 & 1; if ($pde1 & 0x80) { my $vaddr = ($pml4i << 39) | ($pdpti << 30) | ($pdi << 21); my $paddr = (($pde2 << 32) | $pde1) & 0xFFFFFFE00000; txpage $vaddr, $paddr, 0x200000, $pde1 & 0xFFF; next; } my $pt = (($pde2 << 32) | $pde1) & 0xFFFFFFFFF000; printf " Using PD entry %03x %12x f=%012b\n", $pdi << 1, $pt, $pde1 & 0xFFF; for my $pti (0..0x1FF) { my $pte1 = xp $pt + $pti * 0x8; my $pte2 = xp $pt + $pti * 0x8 + 4; next unless $pte1 & 1; my $vaddr = ($pml4i << 39) | ($pdpti << 30) | ($pdi << 21) | ($pti << 12); my $paddr = (($pte2 << 32) | $pte1) & 0xFFFFFFFFF000; txpage $vaddr, $paddr, 0x1000, $pde1 & 0xFFF; } txpage 1,1,1; } txpage 1,1,1; } txpage 1,1,1; }