windbg kernel debugging - How to get the physical address

Viewed 406

I have a module loaded at fffff801`16e00000 and want to get its physical address via !pte but windbg is saying "Levels not implemented for this platform", what gives?

I know how to do i manually by walknig the PML4, PDPT, PD and PT but why isn't this extension working? I'm kernel debugging a virtual machine

1 Answers

I had the same problem multiple times, and I could not figure it out. I don’t know if this will help you, but I made a WinDbg JS script to reproduce the !pte command for this specific case.

It works on 64 bits and only with 4 layer paging.

"use strict";

const system = x => host.namespace.Debugger.Utility.Control.ExecuteCommand(x);
const log = x => host.diagnostics.debugLog(`${x}\n`);
const convertStrToInt64 = str => host.parseInt64(str);

const replaceString = (string, search, replaceWith) => {
    return string.split(search).join(replaceWith);
}

function isBitSet(Nr, Bit)
{
    var mask = host.Int64(1).bitwiseShiftLeft(Bit);
    return Nr.bitwiseAnd(mask).compareTo(0);
}

class PageEntry
{
    constructor(Value)
    {
        this.present = isBitSet(Value, 0);
        this.write = isBitSet(Value, 1);
        this.supervisor = isBitSet(Value, 2);
        this.access = isBitSet(Value, 5);
        this.dirty = isBitSet(Value, 6);
        this.largePage = isBitSet(Value, 7);
        this.exec = isBitSet(Value, 63) == true ? 0 : 1;

        this.value = Value;

        var mask = host.Int64(1);
        mask = mask.bitwiseShiftLeft(36).subtract(1)
        this.pfn = Value.bitwiseShiftRight(12).bitwiseAnd(mask)
    }

    toString() {
        var str = "";

        str += "Value = " + this.value.toString() + "\n\t";
        str += "P(" + this.present + "), ";
        str += "W(" + this.write + "), ";
        str += "E(" + this.exec + "), ";
        str += "A(" + this.access + "), ";
        str += "D(" + this.access + "), ";
        str += "Large(" + this.largePage + ")\n\t";
        str += "Pfn: " + this.pfn.toString();

        return str;
    }
}

function getLayerOffset(Adr, Layer)
{
    var shift = 0;
    switch (Layer)
    {
        case 4:
            shift = 39;
            break;
        case 3:
            shift = 30;
            break;
        case 2:
            shift = 21;
            break;
        case 1:
            shift = 12;
            break;
        default:
            throw EvalError("Invalid layer");
    }
    return Adr.bitwiseShiftRight(shift).bitwiseAnd(0x1ff);
}

function paFromPfn(Pfn)
{
    return Pfn.bitwiseShiftLeft(12)
}

function readDword64FromPa(PhysicalAddress)
{
    var out = system("!dq " + PhysicalAddress.toString() + " L1")[0];
    // output example: #1234abc 12341234`abcdabcd
    // !sometimes the output from !dq has a space after #
    out = out.split(" ");
    var value = out[out.length - 1]
    return convertStrToInt64("0x" + value);
}

function pageWalk(TablePa, Offset, Level)
{
    if (Level == 0)
    {
        return;
    }

    var entryAdr = TablePa.add(Offset[Level-1].multiply(8));
    var entryValue = readDword64FromPa(entryAdr);
    var entry = new PageEntry(entryValue);
    var nextLayerPa = paFromPfn(entry.pfn)

    // print info
    log("Level: " + Level.toString() + ", Entry Address: " + entryAdr.toString())
    log(entry)
    log("")

    if (entry.present == 0)
    {
        log("Next layer not present");
        return;
    }
    if (entry.largePage == 1)
    {
        log("Large page present");
        return;
    }

    pageWalk(nextLayerPa, Offset, Level-1)
}

function getPml4AddressFromCr3(Cr3)
{
    var mask = host.Int64(1);
    var mask = mask.bitwiseShiftLeft(36).subtract(1)
    var pml4Pfn = Cr3.bitwiseShiftRight(12).bitwiseAnd(mask)
    return paFromPfn(pml4Pfn);
}

function translateAddress(VirtualAddress, Cr3Val=NaN)
{
    log("Virtual address: " + VirtualAddress.toString())
    
    var lv4 = getLayerOffset(VirtualAddress, 4);
    var lv3 = getLayerOffset(VirtualAddress, 3);
    var lv2 = getLayerOffset(VirtualAddress, 2);
    var lv1 = getLayerOffset(VirtualAddress, 1);

    if (isNaN(Cr3Val))
    {
        Cr3Val = host.currentThread.Registers.Kernel.cr3;
    }

    var pml4Adr = getPml4AddressFromCr3(Cr3Val);
    log("Pml 4 address: " + pml4Adr.toString())
    pageWalk(pml4Adr, [lv1, lv2, lv3, lv4], 4)
}

function initializeScript()
{
    var app = [new host.apiVersionSupport(1, 7)];

    app.push(new host.functionAlias(translateAddress, "pteV2"));

    return app;
}

function invokeScript()
{
}

To use the script you run .scriptload script.js, then you can use the !pteV2(Virtual_Address) or !pteV2(Virtual_Address, Cr3_Value) instead of !pte.

I don’t have experience with js and the script is not well tested…

The script is on github too.

Related