Freigeben über


Native Debugger-Objekte in JavaScript-Erweiterungen

Systemeigene Debuggerobjekte stellen verschiedene Konstrukte und Verhaltensweisen der Debuggerumgebung dar. Die Objekte können an JavaScript-Erweiterungen übergeben (oder erworben werden), um den Zustand des Debuggers zu bearbeiten.

Beispiele für Debuggerobjekte sind die folgenden:

  • Sitzung
  • Threads / Thread
  • Prozesse und Abläufe
  • Stapelrahmen / Stapelrahmen
  • Lokale Variablen
  • Modules / Module
  • Nützlichkeit
  • Staat
  • Einstellungen

Beispielsweise kann das host.namespace.Debugger.Utility.Control.ExecuteCommand-Objekt verwendet werden, um den U-Befehl mit den folgenden zwei Zeilen JavaScript-Code an den Debugger zu senden.

var ctl = host.namespace.Debugger.Utility.Control;   
var outputLines = ctl.ExecuteCommand("u");

In diesem Thema wird beschrieben, wie Sie mit allgemeinen Objekten arbeiten und Referenzinformationen zu ihren Attributen und Verhaltensweisen bereitstellen.

Allgemeine Informationen zum Arbeiten mit JavaScript finden Sie unter JavaScript Debugger Scripting. JavaScript-Beispiele, die die Debuggerobjekte verwenden, finden Sie unter JavaScript-Debuggerbeispielskripts. Informationen zum Arbeiten mit den Einstellungsobjekten finden Sie unter .settings (Set Debug Settings).

Um die in einer Debuggersitzung verfügbaren Objekte zu erkunden, verwenden Sie den Befehl dx (NatVis Expression anzeigen). Sie können z. B. einige der Debuggerobjekte der obersten Ebene mit diesem DX-Befehl anzeigen.

0: kd> dx -r2 Debugger
Debugger                
    Sessions         : [object Object]
        [0x0]            : Remote KD: KdSrv:Server=@{<Local>},Trans=@{NET:Port=50000,Key=1.2.3.4,Target}
    Settings        
        Debug           
        Display         
        EngineInitialization
        Extensions      
        Input           
        Sources         
        Symbols         
        AutoSaveSettings : false
    State           
        DebuggerVariables
        PseudoRegisters 
        Scripts         
        UserVariables   
    Utility         
        Collections     
        Control         
        Objects   

Alle oben aufgeführten Elemente sind klickbare DML und können weiter unten rekursiert werden, um die Debuggerobjektstruktur anzuzeigen.

Erweitern des Debuggers über das Datenmodell

Das Debuggerdatenmodell ermöglicht die Erstellung einer Schnittstelle zu Informationen zu Anwendungen und Treibern in Windows mit den folgenden Attributen.

  • Ist auffindbar und organisiert– ein logisch strukturierter Namensraum kann mithilfe des Dx-Befehls abgefragt werden.
  • Kann mithilfe von LINQ abgefragt werden. Dies ermöglicht die Extraktion und Sortierung von Daten mithilfe einer Standardabfragesprache.
  • Kann logisch und konsistent erweitert werden – Erweiterbar mit techniken, die in diesem Thema mit Debuggerskriptanbietern wie Natvis und JavaScript beschrieben werden.

Erweitern eines Debuggerobjekts in JavaScript

Neben der Möglichkeit, einen Visualisierer in JavaScript zu erstellen, können Skripterweiterungen auch die Kernkonzepte des Debuggers ändern – Sitzungen, Prozesse, Threads, Stapel, Stapelframes, lokale Variablen – und sich sogar als Erweiterungspunkte veröffentlichen, die von anderen Erweiterungen genutzt werden können.

In diesem Abschnitt wird beschrieben, wie Sie ein Kernkonzept innerhalb des Debuggers erweitern. Erweiterungen, die für die Freigabe erstellt werden, sollten den Richtlinien entsprechen, die in nativen Debuggerobjekten in JavaScript-Erweiterungen – Entwurfs- und Testüberlegungen dargestellt werden.

Registrieren einer Erweiterung

Ein Skript kann die Tatsache registrieren, dass es eine Erweiterung über einen Eintrag im Array bereitstellt, der von der initializeScript-Methode zurückgegeben wird.

function initializeScript()
{
    return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process")];
}

Das Vorhandensein eines host.namedModelParent-Objekts innerhalb des zurückgegebenen Arrays gibt an, dass ein bestimmtes Prototypobjekt oder eine ES6-Klasse (comProcessExtension in diesem Fall) ein übergeordnetes Datenmodell für das Modell sein wird, das unter dem Namen Debugger.Models.Process registriert ist.

Debugger-Objekterweiterungspunkte

Die folgenden Debuggererweiterungspunkte sind integraler Bestandteil des Debuggers und verfügbar für die Verwendung von Skriptanbietern wie JavaScript.

Debugger.Models.Sessions: Die Liste der Sitzungen (Ziele), an die der Debugger angefügt ist

Debugger.Models.Session: Eine einzelne Sitzung (Ziel), an die der Debugger angefügt ist (Live-Benutzermodus, KD usw....)

Debugger.Models.Processes: Die Liste der Prozesse innerhalb einer Sitzung

Debugger.Models.Threads: Die Liste der Threads innerhalb eines Prozesses

Debugger.Models.Thread: Ein einzelner Thread innerhalb eines Prozesses (unabhängig davon, ob Benutzer- oder Kernelmodus)

Debugger.Models.Stack: Der Stapel eines Threads

Debugger.Models.StackFrames: Die Sammlung von Frames, die einen Stapel bilden

Debugger.Models.StackFrame: Ein einzelner Stapelrahmen innerhalb eines Stapels

Debugger.Models.LocalVariables: Die lokalen Variablen innerhalb eines Stapelframes

Debugger.Models.Parameters: Die Parameter für einen Aufruf innerhalb eines Stapelframes

Debugger.Models.Module: Ein einzelnes Modul innerhalb des Adressraums eines Prozesses

Zusätzliche Datenmodellobjekte

Darüber hinaus gibt es einige zusätzliche Datenmodellobjekte, die vom Kerndatenmodell definiert werden.

DataModel.Models.Systemintern: Ein systeminterner Wert (Ordnungszahlen, Floats usw....)

DataModel.Models.String: Eine Zeichenfolge

DataModel.Models.Array: Ein natives Array

DataModel.Models.Guid: Eine GUID

DataModel.Models.Error: Ein Fehlerobjekt

DataModel.Models.Concepts.Iterable: Angewendet auf jedes Objekt, das iterierbar ist

DataModel.Models.Concepts.StringDisplayable: Angewendet auf jedes Objekt, das eine Anzeigezeichenfolgenumwandlung besitzt

Übersicht über die Erweiterung des COM-Debugger-Objekts

Wir sehen uns hierzu ein Beispiel an. Stellen Sie sich vor, Sie möchten eine Debuggererweiterung erstellen, um Spezifische Informationen für COM anzuzeigen, z. B. die globale Schnittstellentabelle (GIT).

In der Vergangenheit kann es eine vorhandene Debuggererweiterung mit einer Reihe von Befehlen geben, die einen Zugriff auf Dinge über COM ermöglichen. Ein Befehl zeigt möglicherweise prozessorientierte Informationen an (z. B. die globale Schnittstellentabelle). Ein anderer Befehl könnte threadorientierte Informationen bereitstellen, wie z. B. in welchem Apartmentcode gerade ausgeführt wird. Möglicherweise müssen Sie eine zweite Debuggererweiterung kennen und laden, um andere Aspekte von COM zu erkunden.

Anstatt eine Reihe von schwer zu erkennenden Befehlen zu haben, kann eine JavaScript-Erweiterung das Konzept des Debuggers ändern, was ein Prozess und ein Thread ist, um diese Informationen auf eine Weise hinzuzufügen, die natürlich, explorierbar und mit anderen Debuggererweiterungen erstellt werden kann.

Benutzer- oder Kernelmodus-Debuggerobjekterweiterung

Der Debugger und die Debuggerobjekte weisen unterschiedliche Verhaltensweisen im Benutzer- und Kernelmodus auf. Wenn Sie Ihre Debuggermodellobjekte erstellen, müssen Sie entscheiden, in welchen Umgebungen Sie arbeiten werden. Da wir mit COM im Benutzermodus arbeiten, erstellen und testen wir diese Com-Erweiterung im Benutzermodus. In anderen Situationen können Sie möglicherweise einen Debugger-JavaScript erstellen, der sowohl im Benutzer- als auch im Kernelmodusdebugging funktioniert.

Erstellen eines Unternamespaces

In unserem Beispiel können wir einen Prototyp oder eine ES6-Klasse definieren, comProcessExtension , die den Satz von Elementen enthält, die wir einem Prozessobjekt hinzufügen möchten.

Wichtig Die Absicht mit dem Unternamespace besteht darin, ein logisch strukturiertes und natürlich explorierbares Paradigma zu erstellen. Vermeiden Sie beispielsweise das Dumping nicht verwandter Elemente im selben Unternamespace. Überprüfen Sie sorgfältig die informationen, die in nativen Debuggerobjekten in JavaScript-Erweiterungen erläutert werden– Entwurfs- und Testüberlegungen , bevor Sie einen Unternamespace erstellen.

In diesem Codeausschnitt erstellen wir einen Unternamespace namens "COM" zum vorhandenen Prozessdebuggerobjekt.

var comProcessExtension =
{
    //
    // Add a sub-namespace called 'COM' on process.
    //
    get COM()
    {
        //
        // What is 'this' below...?  It's the debugger's process object.  Yes -- this means that there is a cross-language
        // object hierarchy here.  A C++ object implemented in the debugger has a parent model (prototype) which is
        // implemented in JavaScript.
        //
        return new comNamespace(this);
    }
}

Namespaceimplementierung

Erstellen Sie als Nächstes das Objekt, das den Unternamespace COM in einem Prozess implementiert.

Wichtig Es können mehrere Prozesse ausgeführt werden, unabhängig davon, ob sie im Benutzermodus angehängt sind oder unter KD. Diese Erweiterung kann nicht davon ausgehen, dass der aktuelle Zustand des Debuggers der vom Benutzer beabsichtigte Zustand ist. Jemand kann someProcess>.COM in einer Variablen erfassen <und ändern, was dazu führen kann, Informationen aus dem falschen Prozesskontext darzustellen. Die Lösung besteht darin, Code in der Erweiterung einzufügen, damit jede Instanziierung nachverfolgt, an welchen Prozess sie angefügt ist. Für dieses Codebeispiel werden diese Informationen über den 'this'-Zeiger des Attributs übergeben.

this.__process = process;

class comNamespace
{
    constructor(process)
    {
        //
        // This is an entirely JavaScript object.  Each instantiation of a comNamespace will keep track
        // of what process it is attached to (passed via the ''this'' pointer of the property getter
        // we authored above.
        //
        this.__process = process;
    }
    
    get GlobalObjects()
    {
        return new globalObjects(this.__process);
    }
}

Implementierungslogik für die globale COM-Schnittstellentabelle

Um die Implementierungslogik für die globale COM-Schnittstellentabelle deutlicher zu trennen, definieren wir eine ES6-Klasse, gipTable , die die COM GIP-Tabelle abstrahiert und eine andere, globalObjects, die aus dem im oben gezeigten Namespaceimplementierungscode definierten GlobalObjects()-Getter zurückgegeben wird. Alle diese Details können innerhalb des Schließens von initializeScript ausgeblendet werden, um die Veröffentlichung dieser internen Details im Debuggernamespace zu vermeiden.

// gipTable:
//
// Internal class which abstracts away the GIP Table.  It iterates objects of the form
// {entry : GIPEntry, cookie : GIT cookie}
//
class gipTable
{
    constructor(gipProcess)
    {
        //
        // Windows 8 through certain builds of Windows 10, it's in CGIPTable::_palloc.  In certain builds
        // of Windows 10 and later, this has been moved to GIPEntry::_palloc.  We need to check which.
        //
        var gipAllocator = undefined;
        try
        {
            gipAllocator = host.getModuleSymbol("combase.dll", "CGIPTable::_palloc", "CPageAllocator", gipProcess)._pgalloc;
        }
        catch(err)
        {
        }

        if (gipAllocator == undefined)
        {
            gipAllocator = host.getModuleSymbol("combase.dll", "GIPEntry::_palloc", "CPageAllocator", gipProcess)._pgalloc;
        }

        this.__data = {
            process : gipProcess,
            allocator : gipAllocator,
            pageList : gipAllocator._pPageListStart,
            pageCount : gipAllocator._cPages,
            entriesPerPage : gipAllocator._cEntriesPerPage,
            bytesPerEntry : gipAllocator._cbPerEntry,
            PAGESHIFT : 16,
            PAGEMASK : 0x0000FFFF,
            SEQNOMASK : 0xFF00
        };
    }

    *[Symbol.iterator]()
    {
        for (var pageNum = 0; pageNum < this.__data.pageCount; ++pageNum)
        {
            var page = this.__data.pageList[pageNum];
            for (var entryNum = 0; entryNum < this.__data.entriesPerPage; ++entryNum)
            {
                var entryAddress = page.address.add(this.__data.bytesPerEntry * entryNum);
                var gipEntry = host.createPointerObject(entryAddress, "combase.dll", "GIPEntry *", this.__data.process);
                if (gipEntry.cUsage != -1 && gipEntry.dwType != 0)
                {
                    yield {entry : gipEntry, cookie : (gipEntry.dwSeqNo | (pageNum << this.__data.PAGESHIFT) | entryNum)};
                }
            }
        }
    }

    entryFromCookie(cookie)
    {
        var sequenceNo = (cookie & this.__data.SEQNOMASK);
        cookie = cookie & ~sequenceNo;
        var pageNum = (cookie >> this.__data.PAGESHIFT);
        if (pageNum < this.__data.pageCount)
        {
            var page = this.__data.pageList[pageNum];
            var entryNum = (cookie & this.__data.PAGEMASK);
            if (entryNum < this.__data.entriesPerPage)
            {
                var entryAddress = page.address.add(this.__data.bytesPerEntry * entryNum);
                var gipEntry = host.createPointerObject(entryAddress, "combase.dll", "GIPEntry *", this.__data.process);
                if (gipEntry.cUsage != -1 && gipEntry.dwType != 0 && gipEntry.dwSeqNo == sequenceNo)
                {
                    return {entry : gipEntry, cookie : (gipEntry.dwSeqNo | (pageNum << this.__data.PAGESHIFT) | entryNum)};
                }
            }
        }

        //
        // If this exception flows back to C/C++, it will be a failed HRESULT (according to the type of error -- here E_BOUNDS)
        // with the message being encapsulated by an error object.
        //
        throw new RangeError("Unable to find specified value");
    }
}
// globalObjects:
//
// The class which presents how we want the GIP table to look to the data model.  It iterates the actual objects
// in the GIP table indexed by their cookie.
//
class globalObjects
{
    constructor(process)
    {
        this.__gipTable = new gipTable(process);
    }

    *[Symbol.iterator]()
    {
        for (var gipCombo of this.__gipTable)
        {
            yield new host.indexedValue(gipCombo.entry.pUnk, [gipCombo.cookie]);
        }
    }

    getDimensionality()
    {
        return 1;
    }

    getValueAt(cookie)
    {
        return this.__gipTable.entryFromCookie(cookie).entry.pUnk;
    }
}

Verwenden Sie schließlich host.namedModelRegistration, um die neue COM-Funktionalität zu registrieren.

function initializeScript()
{
    return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process"),
            new host.namedModelRegistration(comNamespace, "Debugger.Models.ComProcess")];
}

Speichern Sie den Code in GipTableAbstractor.js mit einer Anwendung wie dem Editor.

Hier sehen Sie die Prozessinformationen, die im Benutzermodus verfügbar sind, bevor Sie diese Erweiterung laden.

0:000:x86> dx @$curprocess
@$curprocess                 : DataBinding.exe
    Name             : DataBinding.exe
    Id               : 0x1b9c
    Threads         
    Modules  

Laden Sie die JavaScript-Erweiterung.

0:000:x86> .scriptload C:\JSExtensions\GipTableAbstractor.js
JavaScript script successfully loaded from 'C:\JSExtensions\GipTableAbstractor.js'

Verwenden Sie dann den Dx-Befehl, um Informationen zum Prozess mithilfe der vordefinierten @$curprocess anzuzeigen.

0:000:x86> dx @$curprocess
@$curprocess                 : DataBinding.exe
    Name             : DataBinding.exe
    Id               : 0x1b9c
    Threads         
    Modules         
    COM              : [object Object]
0:000:x86> dx @$curprocess.COM
@$curprocess.COM                 : [object Object]
    GlobalObjects    : [object Object]
0:000:x86> dx @$curprocess.COM.GlobalObjects
@$curprocess.COM.GlobalObjects                 : [object Object]
    [0x100]          : 0x12f4fb0 [Type: IUnknown *]
    [0x201]          : 0x37cfc50 [Type: IUnknown *]
    [0x302]          : 0x37ea910 [Type: IUnknown *]
    [0x403]          : 0x37fcfe0 [Type: IUnknown *]
    [0x504]          : 0x12fe1d0 [Type: IUnknown *]
    [0x605]          : 0x59f04e8 [Type: IUnknown *]
    [0x706]          : 0x59f0eb8 [Type: IUnknown *]
    [0x807]          : 0x59f5550 [Type: IUnknown *]
    [0x908]          : 0x12fe340 [Type: IUnknown *]
    [0xa09]          : 0x5afcb58 [Type: IUnknown *]

Diese Tabelle ist auch programmgesteuert über GIT-Cookie zugänglich.

0:000:x86> dx @$curprocess.COM.GlobalObjects[0xa09]
@$curprocess.COM.GlobalObjects[0xa09]                 : 0x5afcb58 [Type: IUnknown *]
    [+0x00c] __abi_reference_count [Type: __abi_FTMWeakRefData]
    [+0x014] __capture        [Type: Platform::Details::__abi_CapturePtr]

Erweitern von Debuggerobjektkonzepten mit LINQ

Neben der Möglichkeit, Objekte wie Prozess und Thread zu erweitern, kann JavaScript auch Konzepte erweitern, die dem Datenmodell zugeordnet sind. Beispielsweise ist es möglich, jeder iterierbaren Sammlung eine neue LINQ-Methode hinzuzufügen. Betrachten Sie eine Beispielerweiterung "DuplicateDataModel", die jeden Eintrag in einem iterierbaren N-Mal dupliziert. Der folgende Code zeigt, wie dies implementiert werden kann.

function initializeScript()
{
    var newLinqMethod =
    {
        Duplicate : function *(n)
        {
            for (var val of this)
            {
                for (var i = 0; i < n; ++i)
                {
                    yield val;
                }
            };
        }
    };

    return [new host.namedModelParent(newLinqMethod, "DataModel.Models.Concepts.Iterable")];
}

Speichern Sie den Code an DuplicateDataModel.js mithilfe einer Anwendung wie Notizblock.

Laden Sie den JavaScript-Skriptinganbieter bei Bedarf, und laden Sie dann die DuplicateDataModel.js-Erweiterung.

0:000:x86> !load jsprovider.dll
0:000:x86> .scriptload C:\JSExtensions\DuplicateDataModel.js
JavaScript script successfully loaded from 'C:\JSExtensions\DuplicateDataModel.js'

Verwenden Sie den Dx-Befehl, um die neue Duplikatfunktion zu testen.

0: kd> dx -r1 Debugger.Sessions.First().Processes.First().Threads.Duplicate(2),d
Debugger.Sessions.First().Processes.First().Threads.Duplicate(2),d                 : [object Generator]
    [0]              : nt!DbgBreakPointWithStatus (fffff800`9696ca60) 
    [1]              : nt!DbgBreakPointWithStatus (fffff800`9696ca60) 
    [2]              : intelppm!MWaitIdle+0x18 (fffff805`0e351348) 
    [3]              : intelppm!MWaitIdle+0x18 (fffff805`0e351348) 
…

Siehe auch

Native Debugger-Objekte in JavaScript-Erweiterungen – Debugger-Objektdetails

Native Debugger-Objekte in JavaScript-Erweiterungen – Entwurfs- und Testüberlegungen

JavaScript-Debugging-Skripting

JavaScript-Debuggerbeispielskripts