다음을 통해 공유


JavaScript 확장의 네이티브 디버거 개체

네이티브 디버거 개체는 디버거 환경의 다양한 구문과 동작을 나타냅니다. 개체를 JavaScript 확장으로 전달(또는 획득)하여 디버거의 상태를 조작할 수 있습니다.

디버거 개체의 예는 다음과 같습니다.

  • 세션
  • 스레드/스레드
  • 프로세스/프로세스
  • 스택 프레임/스택 프레임
  • 지역 변수
  • 모듈/모듈
  • 유틸리티
  • 시스템 상태
  • 설정

예를 들어 host.namespace.Debugger.Utility.Control.ExecuteCommand 개체를 사용하여 다음 두 줄의 JavaScript 코드를 사용하여 u 명령을 디버거로 보낼 수 있습니다.

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

이 항목에서는 일반적인 개체를 사용하는 방법을 설명하고 해당 특성 및 동작에 대한 참조 정보를 제공합니다.

JavaScript 작업에 대한 일반적인 내용은 JavaScript 디버거 스크립팅을 참조하세요. 디버거 개체를 사용하는 JavaScript 예제는 JavaScript 디버거 예제 스크립트를 참조하세요. 설정 개체 작업에 대한 자세한 내용은 .settings(디버그 설정 설정)를 참조하세요.

디버거 세션에서 사용할 수 있는 개체를 탐색하려면 dx(NatVis 식 표시) 명령을 사용합니다. 예를 들어 이 dx 명령을 사용하여 최상위 디버거 개체 중 일부를 표시할 수 있습니다.

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   

위에 나열된 모든 항목은 클릭 가능한 DML이며 디버거 개체 구조를 보기 위해 더 아래로 재귀할 수 있습니다.

데이터 모델을 통해 디버거 확장

디버거 데이터 모델을 사용하면 다음과 같은 특성이 있는 Windows의 애플리케이션 및 드라이버에 대한 정보에 대한 인터페이스를 만들 수 있습니다.

  • 검색 가능하고 구성됩니다. dx 명령을 사용하여 논리적으로 구조화된 이름 공간을 쿼리할 수 있습니다.
  • LINQ를 사용하여 쿼리할 수 있습니다. 이렇게 하면 표준 쿼리 언어를 사용하여 데이터를 추출하고 정렬할 수 있습니다.
  • 논리적이고 일관되게 확장할 수 있습니다. 이 항목에 설명된 기술을 사용하여 Natvis 및 JavaScript와 같은 디버거 스크립팅 공급자를 사용할 수 있습니다.

JavaScript에서 디버거 개체 확장

스크립트 확장은 JavaScript에서 시각화 도우미를 만들 수 있을 뿐만 아니라 세션, 프로세스, 스레드, 스택, 스택 프레임, 지역 변수 등 디버거의 핵심 개념을 수정하고 다른 확장에서 사용할 수 있는 확장 지점으로 게시할 수도 있습니다.

이 섹션에서는 디버거 내에서 핵심 개념을 확장하는 방법을 설명합니다. 공유하도록 빌드된 확장은 JavaScript 확장 - 디자인 및 테스트 고려 사항의 네이티브 디버거 개체에 제시된 지침을 따라야 합니다.

확장 등록

스크립트는 initializeScript 메서드에서 반환된 배열의 항목을 통해 확장을 제공한다는 사실을 등록할 수 있습니다.

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

반환된 배열 내에 host.namedModelParent 개체가 있으면 지정된 프로토타입 개체 또는 ES6 클래스(이 경우 comProcessExtension)가 Debugger.Models.Process라는 이름으로 등록된 모델에 대한 부모 데이터 모델이 될 것임을 디버거에 나타냅니다.

디버거 개체 확장 지점

다음 디버거 확장 지점은 디버거에 필수적이며 JavaScript와 같은 스크립트 공급자에서 사용할 수 있습니다.

Debugger.Models.Sessions: 디버거가 연결된 세션(대상) 목록

Debugger.Models.Session: 디버거가 연결된 개별 세션(대상)(라이브 사용자 모드, KD 등)입니다.

Debugger.Models.Processes: 세션 내 프로세스 목록

Debugger.Models.Threads: 프로세스 내의 스레드 목록

Debugger.Models.Thread: 프로세스 내의 개별 스레드(사용자 또는 커널 모드 여부에 관계 없이)

Debugger.Models.Stack: 스레드 스택

Debugger.Models.StackFrames: 스택을 구성하는 프레임 컬렉션입니다.

Debugger.Models.StackFrame: 스택 내의 개별 스택 프레임

Debugger.Models.LocalVariables: 스택 프레임 내의 지역 변수

Debugger.Models.Parameters: 스택 프레임 내의 호출에 대한 매개 변수입니다.

Debugger.Models.Module: 프로세스의 주소 공간 내 개별 모듈

추가 데이터 모델 개체

또한 핵심 데이터 모델에 의해 정의된 몇 가지 추가 데이터 모델 개체가 있습니다.

DataModel.Models.Intrinsic: 내장 값(서수, 부동 소수자 등)입니다.

DataModel.Models.String: 문자열

DataModel.Models.Array: 네이티브 배열

DataModel.Models.Guid: GUID

DataModel.Models.Error: 오류 개체

DataModel.Models.Concepts.Iterable: 반복 가능한 모든 개체에 적용

DataModel.Models.Concepts.StringDisplayable: 표시 문자열 변환이 있는 모든 개체에 적용됨

예제 COM 디버거 개체 확장 개요

예제를 살펴보겠습니다. GIT(전역 인터페이스 테이블)와 같은 COM 관련 정보를 표시하는 디버거 확장을 만들려고 합니다.

과거에는 COM에 대한 항목에 액세스하는 방법을 제공하는 여러 명령이 포함된 기존 디버거 확장이 있을 수 있습니다. 한 명령은 프로세스 중심 정보(instance 전역 인터페이스 테이블)를 표시할 수 있습니다. 또 다른 명령은 어떤 아파트 코드가 실행되는지와 같은 스레드 중심 정보를 제공할 수 있습니다. COM의 다른 측면을 살펴보려면 두 번째 디버거 확장에 대해 알고 로드해야 할 수 있습니다.

JavaScript 확장은 명령을 검색하기 어려운 집합을 갖는 대신 프로세스 및 스레드에 대한 디버거의 개념을 수정하여 이 정보를 다른 디버거 확장과 함께 자연스럽고 탐색 가능하며 구성 가능한 방식으로 추가할 수 있습니다.

사용자 또는 커널 모드 디버거 개체 확장

디버거 및 디버거 개체는 사용자 및 커널 모드에서 서로 다른 동작을 갖습니다. 디버거 모델 개체를 만들 때 작업할 환경을 결정해야 합니다. 사용자 모드에서 COM을 사용할 예정이므로 사용자 모드에서 이 com 확장을 만들고 테스트합니다. 다른 상황에서는 사용자 및 커널 모드 디버깅 모두에서 작동하는 디버거 JavaScript를 만들 수 있습니다.

하위 네임스페이스 만들기

예제로 돌아가서 프로세스 개체에 추가하려는 항목 집합을 포함하는 프로토타입 또는 ES6 클래스 comProcessExtension 을 정의할 수 있습니다.

중요 하위 네임스페이스의 의도는 논리적으로 구조화되고 자연스럽게 탐색 가능한 패러다임을 만드는 것입니다. 예를 들어 관련 없는 항목을 동일한 하위 네임스페이스에 덤프하지 않도록 합니다. 하위 네임스페이스를 만들기 전에 JavaScript 확장 - 디자인 및 테스트 고려 사항의 네이티브 디버거 개체에 설명된 정보를 주의 깊게 검토합니다.

이 코드 조각에서는 기존 프로세스 디버거 개체에 'COM'이라는 하위 네임스페이스를 추가합니다.

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);
    }
}

네임스페이스 구현

다음으로, 프로세스에서 하위 네임스페이스 COM을 구현하는 개체를 만듭니다.

중요 여러 프로세스(사용자 모드에서 또는 KD에서 연결됨)가 있을 수 있습니다. 이 확장은 디버거의 현재 상태가 사용자가 의도한 것이라고 가정할 수 없습니다. 누군가가 변수에서 someProcess.COM>을 캡처<하고 수정할 수 있으므로 잘못된 프로세스 컨텍스트에서 정보를 표시할 수 있습니다. 솔루션은 각 인스턴스화가 연결된 프로세스를 추적할 수 있도록 확장에 코드를 추가하는 것입니다. 이 코드 샘플의 경우 이 정보는 속성의 'this' 포인터를 통해 전달됩니다.

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);
    }
}

COM 전역 인터페이스 테이블에 대한 구현 논리

COM 전역 인터페이스 테이블의 구현 논리를 보다 명확하게 구분하기 위해 위에 표시된 네임스페이스 구현 코드 캡처에 정의된 GlobalObjects() getter에서 반환되는 ES6 클래스, COM GIP 테이블을 추상화하는 gipTable 및 다른 globalObjects를 정의합니다. 이러한 모든 세부 정보는 디버거 네임스페이스에 이러한 내부 세부 정보를 게시하지 않도록 initializeScript의 닫기 내에 숨겨질 수 있습니다.

// 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;
    }
}

마지막으로 host.namedModelRegistration을 사용하여 새 COM 기능을 등록합니다.

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

메모장과 같은 애플리케이션을 사용하여 GipTableAbstractor.js 코드를 저장합니다.

다음은 이 확장을 로드하기 전에 사용자 모드에서 사용할 수 있는 프로세스 정보입니다.

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

JavaScript 확장을 로드합니다.

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

그런 다음 dx 명령을 사용하여 미리 정의된 @$curprocess 사용하여 프로세스에 대한 정보를 표시합니다.

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 *]

이 테이블은 GIT 쿠키를 통해 프로그래밍 방식으로 액세스할 수도 있습니다.

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]

LINQ를 사용하여 디버거 개체 개념 확장

JavaScript는 프로세스 및 스레드와 같은 개체를 확장할 수 있을 뿐만 아니라 데이터 모델과 관련된 개념도 확장할 수 있습니다. 예를 들어 모든 반복 가능 개체에 새 LINQ 메서드를 추가할 수 있습니다. 반복 가능한 N번에서 모든 항목을 복제하는 "DuplicateDataModel" 확장 예제를 생각해 보세요. 다음 코드는 이를 구현하는 방법을 보여줍니다.

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")];
}

메모장과 같은 애플리케이션을 사용하여 DuplicateDataModel.js 코드를 저장합니다.

필요한 경우 JavaScript 스크립팅 공급자를 로드한 다음 DuplicateDataModel.js 확장을 로드합니다.

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

dx 명령을 사용하여 새 Duplicate 함수를 테스트합니다.

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) 
…

추가 정보

JavaScript 확장의 네이티브 디버거 개체 - 디버거 개체 세부 정보

JavaScript 확장의 네이티브 디버거 개체 - 디자인 및 테스트 고려 사항

JavaScript 디버거 스크립팅

JavaScript 디버거 예제 스크립트