Full Code of 0vercl0k/windbg-scripts for AI

master f8e01a9783e5 cached
26 files
124.7 KB
34.8k tokens
267 symbols
1 requests
Download .txt
Repository: 0vercl0k/windbg-scripts
Branch: master
Commit: f8e01a9783e5
Files: 26
Total size: 124.7 KB

Directory structure:
gitextract_yxreilaj/

├── LICENSE
├── Manifest/
│   ├── Manifest.1.xml
│   ├── ManifestVersion.txt
│   └── config.xml
├── README.md
├── basics/
│   ├── Int64.js
│   ├── breakpoint.js
│   ├── breakpoint2.js
│   ├── eval.js
│   ├── extendmodel.js
│   ├── extendmodel_1.js
│   ├── extendmodel_2.js
│   ├── extendmodel_2_1.js
│   └── readmemory.js
├── codecov/
│   ├── README.md
│   └── codecov.js
├── gdt/
│   ├── README.md
│   └── gdt.js
├── parse_eh_win64/
│   ├── README.md
│   └── parse_eh_win64.js
├── policybuffer/
│   ├── README.md
│   └── policybuffer.js
├── sm/
│   ├── README.md
│   └── sm.js
└── telescope/
    ├── README.md
    └── telescope.js

================================================
FILE CONTENTS
================================================

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Axel Souchet

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Manifest/Manifest.1.xml
================================================
<?xml version='1.0' encoding='utf-8'?>
<ExtensionPackages Version='1.0.0.0' Compression='none'>
    <ExtensionPackage>
        <Name>Telescope</Name>
        <Version>1.0.0.1</Version>
        <Description>Telescope data dereference</Description>
        <Components>
            <ScriptComponent Name='Telescope' Type='Engine' File='..\telescope\telescope.js' FilePathKind='RepositoryRelative'>
                <FunctionAliases>
                    <FunctionAlias Name='telescope'>
                        <AliasItem>
                            <Syntax>
                                <![CDATA[!telescope <address>]]>
                            </Syntax>
                            <Description>
                                <![CDATA[Telescope dereference.]]>
                            </Description>
                        </AliasItem>
                    </FunctionAlias>
                </FunctionAliases>
            </ScriptComponent>
        </Components>
    </ExtensionPackage>

    <ExtensionPackage>
        <Name>Gdt</Name>
        <Version>1.0.0.1</Version>
        <Description>Gdt dump</Description>
        <Components>
            <ScriptComponent Name='Gdt' Type='Engine' File='..\gdt\gdt.js' FilePathKind='RepositoryRelative'>
                <FunctionAliases>
                    <FunctionAlias Name='gdt'>
                        <AliasItem>
                            <Syntax>
                                <![CDATA[!gdt <segment selector>]]>
                            </Syntax>
                            <Description>
                                <![CDATA[Dump the GDT or a GDT entry.]]>
                            </Description>
                        </AliasItem>
                    </FunctionAlias>
                </FunctionAliases>
            </ScriptComponent>
        </Components>
    </ExtensionPackage>

    <ExtensionPackage>
        <Name>sm</Name>
        <Version>1.0.0.1</Version>
        <Description>Pretty printers for JS::Value and JSObject objects in SpiderMonkey</Description>
        <Components>
            <ScriptComponent Name='sm' Type='Engine' File='..\sm\sm.js' FilePathKind='RepositoryRelative'>
                <LoadTriggers>
                    <TriggerSet>
                        <ModuleTrigger Name='xul.dll' />
                    </TriggerSet>
                    <TriggerSet>
                        <ModuleTrigger Name='js.exe' />
                    </TriggerSet>
                </LoadTriggers>
                <FunctionAliases>
                    <FunctionAlias Name='smdump_jsvalue'>
                        <AliasItem>
                            <Syntax>
                                <![CDATA[!smdump_jsvalue <address>]]>
                            </Syntax>
                            <Description>
                                <![CDATA[Pretty print a JS::Value object.]]>
                            </Description>
                        </AliasItem>
                    </FunctionAlias>
                    <FunctionAlias Name='smdump_jsobject'>
                        <AliasItem>
                            <Syntax>
                                <![CDATA[!smdump_jsobject <address>]]>
                            </Syntax>
                            <Description>
                                <![CDATA[Pretty print a JSObject object.]]>
                            </Description>
                        </AliasItem>
                    </FunctionAlias>
                    <FunctionAlias Name='ion_insertbp'>
                        <AliasItem>
                            <Syntax>
                                <![CDATA[!ion_insertbp]]>
                            </Syntax>
                            <Description>
                                <![CDATA[Insert an int3 in the assembly buffer to debug JIT'd code.]]>
                            </Description>
                        </AliasItem>
                    </FunctionAlias>
                    <FunctionAlias Name='in_nursery'>
                        <AliasItem>
                            <Syntax>
                                <![CDATA[!in_nursery]]>
                            </Syntax>
                            <Description>
                                <![CDATA[Figure out if an object is stored in the Nursery or not.]]>
                            </Description>
                        </AliasItem>
                    </FunctionAlias>

                </FunctionAliases>
            </ScriptComponent>
        </Components>
    </ExtensionPackage>
</ExtensionPackages>


================================================
FILE: Manifest/ManifestVersion.txt
================================================
1
1.0.0.0
1


================================================
FILE: Manifest/config.xml
================================================
<?xml version='1.0' encoding='utf-8'?>
<Settings Version='1'>
    <Namespace Name='Extensions'>
        <Setting Name='ExtensionRepository' Type='VT_BSTR' Value='Implicit'></Setting>
        <Namespace Name='ExtensionRepositories'>
            <Namespace Name='overgallery'>
                <Setting Name='Id' Type='VT_BSTR' Value='690F3350-53EE-4EC4-A95B-750068897ED4'></Setting>
                <Setting Name='LocalCacheRootFolder' Type='VT_BSTR' Value='c:\work\codes\windbg-scripts\Manifest'></Setting>
                <Setting Name='IsEnabled' Type='VT_BOOL' Value='true'></Setting>
            </Namespace>
        </Namespace>
    </Namespace>
</Settings>


================================================
FILE: README.md
================================================
# windbg-scripts

`windbg-scripts` is a collection of [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debugger-scripting) debugger extensions  for WinDbg.

* [basics](basics/): various examples of basic usage of various APIs,
* [parse_eh_win64](parse_eh_win64/): example of extending the data-model with exception handling related information (cf [Debugger data model, Javascript & x64 exception handling](https://doar-e.github.io/blog/2017/12/01/debugger-data-model/)),
* [telescope](telescope/): [telescope](https://gef.readthedocs.io/en/latest/commands/dereference/) like command for WinDbg,
* [sm](sm/): pretty-printing of Spidermonkey `js::Value` and `JSObject` objects,
* [codecov](codecov/): extract code-coverage out of a [TTD](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/time-travel-debugging-overview) trace,
* [policybuffer](policybuffer/): disassemble a Chrome policy buffer program,
* [gdt](gdt/): dump the [Global Descriptor Table](https://wiki.osdev.org/Global_Descriptor_Table).

## Installing the script gallery

If you would like to have `telescope` and `sm` loaded every time your debugger starts instead of loading the extensions manually follow the below steps:

1. Clone this GitHub repository,

2. Edit the `Manifest\config.xml` file and update the `LocalCacheRootFolder` path with a value that makes sense,

3. Open the debugger and import the gallery by running `.settings load c:\path\where\cloned\windbg-scripts\Manifest\config.xml` and `.settings save`.

4. Restart the debugger and you should be able to run `!telescope` as well as inspecting the gallery content from the data-model.

   ```text
   0:000> dx -r1 Debugger.State.ExtensionGallery.ExtensionRepositories
   Debugger.State.ExtensionGallery.ExtensionRepositories                
       [0x0]            : overgallery
       [0x1]            : LocalInstalled
   
   0:000> dx -r1 Debugger.State.ExtensionGallery.ExtensionRepositories[0]
   Debugger.State.ExtensionGallery.ExtensionRepositories[0]                 : overgallery
       Name             : overgallery
       ManifestVersion  : 0x1
       URL             
       Enabled          : true
       Packages        
   
   0:000> dx -r1 Debugger.State.ExtensionGallery.ExtensionRepositories[0].Packages
   Debugger.State.ExtensionGallery.ExtensionRepositories[0].Packages                
       [0x0]            : Telescope
   
   0:000> dx -r1 Debugger.State.ExtensionGallery.ExtensionRepositories[0].Packages[0]
   Debugger.State.ExtensionGallery.ExtensionRepositories[0].Packages[0]                 : Telescope
       Name             : Telescope
       Version          : 1.0.0.1
       Description      : Telescope data dereference
       Size             : 0
       IsDownloaded     : true
       Components      
   ```


================================================
FILE: basics/Int64.js
================================================
// Axel '0vercl0k' Souchet - Dec 2017

"use strict";

let logln = function (e) {
    host.diagnostics.debugLog(e + '\n');
}

function invokeScript() {
    let a = host.Int64(1337);
    let aplusone = a + 1;
    logln(aplusone.toString(16));
    let b = host.parseInt64('0xdeadbeefbaadc0de', 16);
    let bplusone = b.add(1);
    logln(bplusone.toString(16));
    let bplusonenothrow = b.convertToNumber() + 1;
    logln(bplusonenothrow);
    try {
        let bplusonethrow = b + 1;
    } catch(e) {
        logln(e);
    }
    logln(a.compareTo(1));
    logln(a.compareTo(1337));
    logln(a.compareTo(1338));
}


================================================
FILE: basics/breakpoint.js
================================================
// Axel '0vercl0k' Souchet - Dec 2017

"use strict";

let logln = function (e) {
    host.diagnostics.debugLog(e + '\n');
}

function handle_bp() {
    let Regs = host.currentThread.Registers.User;
    let Args = [Regs.rcx, Regs.rdx, Regs.r8];
    let ArgsS = Args.map(c => c.toString(16));
    let HeapHandle = ArgsS[0];
    let Flags = ArgsS[1];
    let Size = ArgsS[2];
    logln('RtlAllocateHeap: HeapHandle: ' + HeapHandle + ', Flags: ' + Flags + ', Size: ' + Size);
}

function invokeScript() {
    let Control = host.namespace.Debugger.Utility.Control;
    let Regs = host.currentThread.Registers.User;
    let CurrentProcess = host.currentProcess;
    let BreakpointAlreadySet = CurrentProcess.Debug.Breakpoints.Any(
        c => c.OffsetExpression == 'ntdll!RtlAllocateHeap+0x0'
    );

    if(BreakpointAlreadySet == false) {
        let Bp = Control.SetBreakpointAtOffset('RtlAllocateHeap', 0, 'ntdll');
        Bp.Command = '.echo doare; dx @$scriptContents.handle_bp(); gc';
    } else {
        logln('Breakpoint already set.');
    }
    logln('Press "g" to run the target.');
    // let Lines = Control.ExecuteCommand('gc');
    // for(let Line of Lines) {
    //     logln('Line: ' + Line);
    // }
}


================================================
FILE: basics/breakpoint2.js
================================================
// Axel '0vercl0k' Souchet - Dec 2017

"use strict";

let logln = function (e) {
    host.diagnostics.debugLog(e + '\n');
}

function handle_bp() {
    let Regs = host.currentThread.Registers.User;
    let Args = [Regs.rcx, Regs.rdx, Regs.r8];
    let ArgsS = Args.map(c => c.toString(16));
    let HeapHandle = ArgsS[0];
    let Flags = ArgsS[1];
    let Size = ArgsS[2];
    logln('RtlAllocateHeap: HeapHandle: ' + HeapHandle + ', Flags: ' + Flags + ', Size: ' + Size);
    if(Args[2].compareTo(0x100) > 0) {
        // stop execution if the allocation size is bigger than 0x100
        return true;
    }
    // keep the execution going if it's a small size
    return false;
}

function invokeScript() {
    let Control = host.namespace.Debugger.Utility.Control;
    let Regs = host.currentThread.Registers.User;
    let CurrentProcess = host.currentProcess;
    let HeapAlloc = host.getModuleSymbolAddress('ntdll', 'RtlAllocateHeap');
    let BreakpointAlreadySet = CurrentProcess.Debug.Breakpoints.Any(
        c => c.Address == HeapAlloc
    );
    if(BreakpointAlreadySet == false) {
        logln('RltAllocateHeap @ ' + HeapAlloc.toString(16));
        Control.ExecuteCommand('bp /w "@$scriptContents.handle_bp()" ' + HeapAlloc.toString(16));
    } else {
        logln('Breakpoint already set.');
    }
    logln('Press "g" to run the target.');
}


================================================
FILE: basics/eval.js
================================================
// Axel '0vercl0k' Souchet - Dec 2017

"use strict";

let logln = function (e) {
    host.diagnostics.debugLog(e + '\n');
}

function invokeScript() {
    let Control = host.namespace.Debugger.Utility.Control;
    for(let Line of Control.ExecuteCommand('kp')) {
        logln('Line: ' + Line);
    }
}


================================================
FILE: basics/extendmodel.js
================================================
// Axel '0vercl0k' Souchet - Dec 2017

"use strict";

class Attribute {
    constructor(Process, Name, Value) {
        this.__process = Process;
        this.Name = Name;
        this.Value = Value;
    }

    toString() {
        let S = 'Process: ' + this.__process.Name + ', ';
        S += 'Name: ' + this.Name + ', ';
        S += 'Value: ' + this.Value;
        return S;
    }
}

class Attributes {
    constructor() {
        this.__attrs = [];
    }

    push(Attr) {
        this.__attrs.push(Attr);
    }

    *[Symbol.iterator]() {
        for (let Attr of this.__attrs) {
            yield Attr;
        }
    }

    toString() {
        return 'Attributes';
    }
}

class Sub {
    constructor(Process) {
        this.__process = Process;
    }

    get SubFoo() {
        return 'SubFoo from ' + this.__process.Name;
    }

    get SubBar() {
        return 'SubBar from ' + this.__process.Name;
    }

    get Attributes() {
        let Attrs = new Attributes();
        Attrs.push(new Attribute(this.__process, 'attr0', 'value0'));
        Attrs.push(new Attribute(this.__process, 'attr1', 'value0'));
        return Attrs;
    }

    toString() {
        return 'Sub module';
    }
}

class DiaryOfAReverseEngineer {
    constructor(Process) {
        this.__process = Process;
    }

    get Foo() {
        return 'Foo from ' + this.__process.Name;
    }

    get Bar() {
        return 'Bar from ' + this.__process.Name;
    }

    Add(a, b) {
        return a + b;
    }

    get Sub() {
        return new Sub(this.__process);
    }

    toString() {
        return 'Diary of a reverse-engineer';
    }
}

class ProcessModelParent {
    get DiaryOfAReverseEngineer() {
        return new DiaryOfAReverseEngineer(this);
    }
}

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


================================================
FILE: basics/extendmodel_1.js
================================================
// Axel '0vercl0k' Souchet - Dec 2017

"use strict";

class ProcessModelParent {
    get DiaryOfAReverseEngineer() {
        return 'hello from ' + this.Name;
    }
}

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

================================================
FILE: basics/extendmodel_2.js
================================================
// Axel '0vercl0k' Souchet - Dec 2017

"use strict";

class DiaryOfAReverseEngineer {
    constructor(Process) {
        this.process = Process;
    }

    get Foo() {
        return 'Foo from ' + this.process.Name;
    }

    get Bar() {
        return 'Bar from ' + this.process.Name;
    }

    Add(a, b) {
        return a + b;
    }
}

class ProcessModelParent {
    get DiaryOfAReverseEngineer() {
        return new DiaryOfAReverseEngineer(this);
    }
}

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

================================================
FILE: basics/extendmodel_2_1.js
================================================
// Axel '0vercl0k' Souchet - Dec 2017

"use strict";

class DiaryOfAReverseEngineer {
    constructor(Process) {
        this.__process = process;
    }

    get Foo() {
        return 'Foo from ' + this.__process.Name;
    }

    get Bar() {
        return 'Bar from ' + this.__process.Name;
    }

    Add(a, b) {
        return a + b;
    }

    toString() {
        return 'Diary of a reverse-engineer';
    }
}

class ProcessModelParent {
    get DiaryOfAReverseEngineer() {
        return new DiaryOfAReverseEngineer(this);
    }
}

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

================================================
FILE: basics/readmemory.js
================================================
// Axel '0vercl0k' Souchet - Dec 2017

"use strict";

let logln = function (e) {
    host.diagnostics.debugLog(e + '\n');
}

function read_u64(addr) {
    return host.memory.readMemoryValues(addr, 1, 8)[0];
}

function invokeScript() {
    let Regs = host.currentThread.Registers.User;
    let a = read_u64(Regs.rsp);
    logln(a.toString(16));
    try {
        read_u64(0xdeadbeef);
    } catch(e) {
        logln(e);
    }
    let WideStr = host.currentProcess.Environment.EnvironmentBlock.ProcessParameters.ImagePathName.Buffer;
    logln(host.memory.readWideString(WideStr));
    let WideStrAddress = WideStr.address;
    logln(host.memory.readWideString(WideStrAddress));
}


================================================
FILE: codecov/README.md
================================================
# codecov.js

`codecov.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debugger-scripting) debugger extension for WinDbg that allows to extract code-coverage out of a [TTD](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/time-travel-debugging-overview) trace. It generates a text file with every offsets in a module that have been executed during the recording.

The file looks like the below:

```text
; TracePath: C:\work\codes\blazefox\js01.run
; c:\windows\system32\kernelbase.dll, 7fffb4ce0000, 293000
kernelbase.dll+5df40
kernelbase.dll+5df43
kernelbase.dll+5df47
kernelbase.dll+5df4b
kernelbase.dll+5df4f
...
; c:\windows\system32\kernel32.dll, 7fffb6460000, b3000
kernel32.dll+1f3a0
kernel32.dll+21bb0
kernel32.dll+1bb90
kernel32.dll+1a280
kernel32.dll+1a284
kernel32.dll+1e640
kernel32.dll+63a0
```

## Usage

Run `.scriptload codecov.js` to load the script. You can extract code-coverage using `!codecov "foo"`.

## Examples

Extract code-coverage for every module having `kernel` in their name:

```text
0:000> !codecov "kernel"
Looking for *kernel*..
Found 2 hits
Found 7815 unique addresses in C:\WINDOWS\System32\KERNELBASE.dll
Found 1260 unique addresses in C:\WINDOWS\System32\KERNEL32.DLL
Writing C:\work\codes\tmp\js01.run.kernel.text...
Done!
@$codecov("kernel")

0:000> !codecov "kernel"
Looking for *kernel*..
The output file C:\work\codes\tmp\js01.run.kernel.text already exists, exiting.
@$codecov("kernel")
```



================================================
FILE: codecov/codecov.js
================================================
// Axel '0vercl0k' Souchet - 2 Feb 2018
//
// Example:
//   0:000> !codecov "kernel"
//   Looking for *kernel*..
//   Found 2 hits
//   Found 7815 unique addresses in C:\WINDOWS\System32\KERNELBASE.dll
//   Found 1260 unique addresses in C:\WINDOWS\System32\KERNEL32.DLL
//   Writing C:\work\codes\tmp\js01.run.kernel.text...
//   Done!
//   @$codecov("kernel")
//   0:000> !codecov "kernel"
//   Looking for *kernel*..
//   The output file C:\work\codes\tmp\js01.run.kernel.text already exists, exiting.
//   @$codecov("kernel")
//

'use strict';

const log = host.diagnostics.debugLog;
const logln = p => host.diagnostics.debugLog(p + '\n');
const hex = p => p.toString(16);

function ExtractModuleName(ModulePath) {
    return ModulePath.slice(
        ModulePath.lastIndexOf('\\') + 1
    );
}

function CodeCoverageModule(Module) {
    const CurrentSession = host.currentSession;
    const BaseAddress = Module.BaseAddress;
    const Size = Module.Size;

    const CoverageLines = CurrentSession.TTD.Memory(
        BaseAddress,
        BaseAddress.add(Size),
        'EC'
    );

    const Offsets = Array.from(CoverageLines).map(
        p => hex(
            p.Address.subtract(BaseAddress)
        )
    );

    return {
        'Path' : Module.Name.toLowerCase(),
        'Base' : BaseAddress,
        'Size' : Size,
        'Offsets' : Offsets
    };
}

function CodeCov(ModulePattern) {
    const CurrentSession = host.currentSession;
    const CurrentProcess = host.currentProcess;
    const Utility = host.namespace.Debugger.Utility;

    if(!CurrentSession.Attributes.Target.IsTTDTarget) {
        logln('!codecov expects a TTD trace');
        return;
    }

    if(ModulePattern == undefined) {
        logln('!codecov "pattern"');
        return;
    }

    ModulePattern = ModulePattern.toLowerCase();
    logln('Looking for *' + ModulePattern + '*..');
    const Modules = CurrentProcess.Modules.Where(
        p => p.Name.toLowerCase().indexOf(ModulePattern) != -1
    );

    if(Modules.Count() == 0) {
        logln('Could not find any matching module, exiting');
        return;
    }

    const TracePath = CurrentSession.Attributes.Target.Details.DumpFileName;
    const TraceDir = TracePath.slice(
        0,
        TracePath.lastIndexOf('\\')
    );
    const TraceName = TracePath.slice(
        TracePath.lastIndexOf('\\') + 1
    );
    const FilePath = TraceDir + '\\' + TraceName + '.' + ModulePattern + '.txt';
    if(Utility.FileSystem.FileExists(FilePath)) {
        logln('The output file ' + FilePath + ' already exists, exiting.');
        return;
    }

    const Metadata = {
        'TracePath' : TracePath
    };

    const CoverageModules = [];
    logln('Found ' + Modules.Count() + ' hits');
    for(const Module of Modules) {
        const ModuleCoverage = CodeCoverageModule(Module);
        logln('Found ' + ModuleCoverage.Offsets.length + ' unique addresses in ' + Module.Name);
        CoverageModules.push(ModuleCoverage);
    }

    logln('Writing ' + FilePath + '...');
    const FileHandle = Utility.FileSystem.CreateFile(FilePath, 'CreateAlways');
    const Writer = Utility.FileSystem.CreateTextWriter(FileHandle);
    for(const [Name, Value] of Object.entries(Metadata)) {
        Writer.WriteLine('; ' + Name + ': ' + Value);
    }

    for(const Module of CoverageModules) {

        //
        // First write the metadata that is module specific.
        //

        Writer.WriteLine('; ' + Module.Path + ', ' + hex(Module.Base) + ', ' + hex(Module.Size));

        //
        // Write down the offsets.
        //

        const ModuleName = ExtractModuleName(Module.Path);
        for(const Offset of Module.Offsets) {
            Writer.WriteLine(ModuleName + '+' + hex(Offset));
        }
    }

    FileHandle.Close();
    logln('Done!');
}

function initializeScript() {
    return [
        new host.apiVersionSupport(1, 2),
        new host.functionAlias(
            CodeCov,
            'codecov'
        )
    ];
}


================================================
FILE: gdt/README.md
================================================
# gdt.js

`gdt.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debugger-scripting) debugger extension for WinDbg that dumps the [Global Descriptor Table](https://wiki.osdev.org/Global_Descriptor_Table) on 64-bit kernels. I wrote this extension because I always find the output of the `dg` command confusing, if not broken.


## Usage

Run `.scriptload gdt.js` to load the script. You can dump a specific entry by passing the segment selector to the `!gdt` command, or it will dump the entire table if nothing is passed. Run `!wow64exts.sw` if you are running the script while being in the context of a [WoW64](https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details) thread.

## Examples

* Dumping the GDT entry that enables [WoW64](https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details):
```
32.kd> !gdt @cs
dt nt!_KGDTENTRY64 0xfffff8045215dfd0
   Base: [0x0 -> 0xffffffff]
   Type: Code Execute/Read Accessed (0xb)
    DPL: 0x3
Present: 0x1
   Mode: 32b Compat
@$gdt(@cs)

32.kd> dg @cs
                                                    P Si Gr Pr Lo
Sel        Base              Limit          Type    l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
0023 00000000`00000000 00000000`ffffffff Code RE Ac 3 Bg Pg P  Nl 00000cfb
```

* Dumping the GDT entry that allows 32-bit code to invoke 64-bit code:
```
32.kd> !gdt 0x33
dt nt!_KGDTENTRY64 0xfffff8045215dfe0
   Base: [0x0 -> 0x0]
   Type: Code Execute/Read Accessed (0xb)
    DPL: 0x3
Present: 0x1
   Mode: 64b
@$gdt(0x33)

32.kd> dg 33
                                                    P Si Gr Pr Lo
Sel        Base              Limit          Type    l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
0033 00000000`00000000 00000000`00000000 Code RE Ac 3 Nb By P  Lo 000002fb
```

* Dumping the [Task State Segment](https://wiki.osdev.org/Task_State_Segment):
```
32.kd> !gdt @tr
dt nt!_KGDTENTRY64 0xfffff8045215dff0
   Base: [0xfffff8045215c000 -> 0xfffff8045215c067]
   Type: TSS64 Busy (0xb)
    DPL: 0x0
Present: 0x1
@$gdt(@tr)

32.kd> dg @tr
                                                    P Si Gr Pr Lo
Sel        Base              Limit          Type    l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
0040 00000000`5215c000 00000000`00000067 TSS32 Busy 0 Nb By P  Nl 0000008b
```

* Dumping the [Thread Environment Block](https://en.wikipedia.org/wiki/Win32_Thread_Information_Block) of a [WoW64](https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details) thread:
```
32.kd> !gdt @fs
dt nt!_KGDTENTRY64 0xfffff8045215e000
   Base: [0x326000 -> 0x329c00]
   Type: Data Read/Write Accessed (0x3)
    DPL: 0x3
Present: 0x1
@$gdt(@fs)

32.kd> !teb
Wow64 TEB32 at 0000000000326000
    ExceptionList:        00000000004ff59c
    StackBase:            0000000000500000
    StackLimit:           00000000004f2000
    SubSystemTib:         0000000000000000
    FiberData:            0000000000001e00
    ArbitraryUserPointer: 0000000000000000
    Self:                 0000000000326000
    EnvironmentPointer:   0000000000000000
    ClientId:             0000000000001ad8 . 0000000000001adc
    RpcHandle:            0000000000000000
    Tls Storage:          0000000000834188
    PEB Address:          0000000000323000
    LastErrorValue:       0
    LastStatusValue:      c000007c
    Count Owned Locks:    0
    HardErrorMode:        0
```
* Dumping the entire GDT on a Windows 10 64-bit Virtual Machine:
```
32.kd> !gdt
Dumping the GDT from 0xfffff8045215dfb0 to 0xfffff8045215e007..
[0]: dt nt!_KGDTENTRY64 0xfffff8045215dfb0
   Base: [0x0 -> 0x0]
   Type: Reserved (0x0)
    DPL: 0x0
Present: 0x0
[1]: dt nt!_KGDTENTRY64 0xfffff8045215dfb8
   Base: [0x0 -> 0x0]
   Type: Reserved (0x0)
    DPL: 0x0
Present: 0x0
[2]: dt nt!_KGDTENTRY64 0xfffff8045215dfc0
   Base: [0x0 -> 0x0]
   Type: Code Execute/Read Accessed (0xb)
    DPL: 0x0
Present: 0x1
   Mode: 64b
[3]: dt nt!_KGDTENTRY64 0xfffff8045215dfc8
   Base: [0x0 -> 0x0]
   Type: Data Read/Write Accessed (0x3)
    DPL: 0x0
Present: 0x1
[4]: dt nt!_KGDTENTRY64 0xfffff8045215dfd0
   Base: [0x0 -> 0xffffffff]
   Type: Code Execute/Read Accessed (0xb)
    DPL: 0x3
Present: 0x1
   Mode: 32b Compat
[5]: dt nt!_KGDTENTRY64 0xfffff8045215dfd8
   Base: [0x0 -> 0xffffffff]
   Type: Data Read/Write Accessed (0x3)
    DPL: 0x3
Present: 0x1
[6]: dt nt!_KGDTENTRY64 0xfffff8045215dfe0
   Base: [0x0 -> 0x0]
   Type: Code Execute/Read Accessed (0xb)
    DPL: 0x3
Present: 0x1
   Mode: 64b
[7]: dt nt!_KGDTENTRY64 0xfffff8045215dfe8
   Base: [0x0 -> 0x0]
   Type: Reserved (0x0)
    DPL: 0x0
Present: 0x0
[8]: dt nt!_KGDTENTRY64 0xfffff8045215dff0
   Base: [0xfffff8045215c000 -> 0xfffff8045215c067]
   Type: TSS64 Busy (0xb)
    DPL: 0x0
Present: 0x1
[9]: dt nt!_KGDTENTRY64 0xfffff8045215e000
   Base: [0x326000 -> 0x329c00]
   Type: Data Read/Write Accessed (0x3)
    DPL: 0x3
Present: 0x1
@$gdt()
```

================================================
FILE: gdt/gdt.js
================================================
// Axel '0vercl0k' Souchet - October 8 2021

'use strict';

//
// Small reminders from the manual intels on segments in x64:
//   - Because ES, DS, and SS segment registers are not used in 64-bit mode, their fields (base, limit, and attribute) in
//   segment descriptor registers are ignored.
//   - Selector.Index selects one of 8192 descriptors in the GDT or LDT. The processor multiplies
//   the index value by 8 (the number of bytes in a segment descriptor) and adds the result to the base
//   address of the GDT or LDT (from the GDTR or LDTR register, respectively).
//   - The first entry of the GDT is not used by the processor.
//   - The hidden descriptor register fields for FS.base and GS.base are physically mapped to MSRs in order to load all
//   address bits supported by a 64-bit implementation. Software with CPL = 0 (privileged software) can load all
//   supported linear-address bits into FS.base or GS.base using WRMSR.
//

const log = host.diagnostics.debugLog;
const logln = p => log(`${p}\n`);
const hex = p => `0x${p.toString(16)}`;

const GdtSystemEntryTypes = new Map([
    [0, 'Reserved'],
    [1, 'Reserved'],
    [2, 'LDT'],
    [3, 'Reserved'],
    [4, 'Reserved'],
    [5, 'Reserved'],
    [6, 'Reserved'],
    [7, 'Reserved'],
    [8, 'Reserved'],
    [9, 'TSS64 Available'],
    [10, 'Reserved'],
    [11, 'TSS64 Busy'],
    [12, 'CallGate64'],
    [13, 'Reserved'],
    [14, 'InterruptGate64'],
    [15, 'TrapGate64'],
]);

const GdtNonSystemEntryTypes = new Map([
    [0, 'Data Read-Only'],
    [1, 'Data Read-Only Accessed'],
    [2, 'Data Read/Write'],
    [3, 'Data Read/Write Accessed'],
    [4, 'Data Read-Only Expand-Down'],
    [5, 'Data Read-Only Expand-Down Accessed'],
    [6, 'Data Read/Write Expand-Down'],
    [7, 'Data Read/Write Expand-Down Accessed'],
    [8, 'Code Execute-Only'],
    [9, 'Code Execute-Only Accessed'],
    [10, 'Code Execute/Read'],
    [11, 'Code Execute/Read Accessed'],
    [12, 'Code Execute-Only Conforming'],
    [13, 'Code Execute-Only Conforming Accessed'],
    [14, 'Code Execute/Read Conforming'],
    [15, 'Code Execute/Read Conforming Accessed'],
]);

const GdtEntryTypes = new Map([
    [0, GdtSystemEntryTypes],
    [1, GdtNonSystemEntryTypes]
]);

class GdtEntry {
    constructor(Addr) {
        this._Addr = Addr;
        const Entry = host.createPointerObject(Addr, 'nt', '_KGDTENTRY64*');
        const LimitHigh = Entry.Bits.LimitHigh.bitwiseShiftLeft(16);
        const LimitLow = Entry.LimitLow;
        this._Limit = LimitHigh.add(LimitLow);
        // For whatever reason _KGDTENTRY64 is 5 bits long. The intel manuals describes
        // it as 4bits and the 'Descriptor type' bit.
        // We grab the lower 4 bits as the type, and the MSB as the 'Descriptor type'.
        this._Type = Entry.Bits.Type & 15;
        this._NonSystem = (Entry.Bits.Type >> 4) & 1;
        this._TypeS = GdtEntryTypes.get(this._NonSystem).get(this._Type);
        // Note that system descriptors in IA-32e mode are 16 bytes instead
        // of 8 bytes.
        this._Size = 8;
        if (!this._NonSystem && this._TypeS != 'Reserved') {
            this._Size = 16;
        }
        this._Dpl = Entry.Bits.Dpl;
        this._Granularity = Entry.Bits.Granularity;
        this._Present = Entry.Bits.Present;
        this._LongMode = Entry.Bits.LongMode;
        this._DefaultBig = Entry.Bits.DefaultBig;
        const BaseUpper = this._Size == 8 ? 0 : Entry.BaseUpper.bitwiseShiftLeft(32);
        const BaseHigh = Entry.Bytes.BaseHigh.bitwiseShiftLeft(24);
        const BaseMiddle = Entry.Bytes.BaseMiddle.bitwiseShiftLeft(16);
        const BaseLow = Entry.BaseLow;
        this._Base = BaseUpper.add(BaseHigh).add(BaseMiddle).add(BaseLow);
        const Flags1 = Entry.Bytes.Flags1;
        const Flags2 = Entry.Bytes.Flags2.bitwiseShiftLeft(8);
        this._Attrs = Flags2.add(Flags1);
    }

    toString() {
        const Increments = this._Granularity == 1 ? 1024 * 4 : 1;
        // For example, when the granularity flag is set, a limit of 0 results in
        // valid offsets from 0 to 4095.
        const Size = this._Limit * Increments + (this._Granularity ? 0xfff : 0);
        let S = `dt nt!_KGDTENTRY64 ${hex(this._Addr)}
     Base: [${hex(this._Base)} -> ${hex(this._Base.add(Size))}]
     Type: ${this._TypeS} (${hex(this._Type)})
      DPL: ${hex(this._Dpl)}
  Present: ${hex(this._Present)}
Atributes: ${hex(this._Attrs)}`;
if (this._TypeS.startsWith('Code')) {
           S += `
     Mode: ${this._LongMode ? '64b' : (this._DefaultBig ? '32b Compat' : '16b Compat')}`
       }
       return S;
    }
}

function GetGdt() {
    const Control = host.namespace.Debugger.Utility.Control;
    const [_, GdtrValue] = Control.ExecuteCommand('r @gdtr').First().split('=');
    const [__, GdtlValue] = Control.ExecuteCommand('r @gdtl').First().split('=');
    return [host.parseInt64(GdtrValue, 16), host.parseInt64(GdtlValue, 16)];
}

function DumpGdtEntry(Addr) {
    return new GdtEntry(Addr);
}

function DumpAllGdt() {
    const [GdtBase, Gdtl] = GetGdt();
    const GdtEnd = GdtBase.add(Gdtl);
    logln(`Dumping the GDT from ${hex(GdtBase)} to ${hex(GdtEnd)}..`);
    for (let CurrentEntry = GdtBase, Idx = 0;
        CurrentEntry.compareTo(GdtEnd) < 0;
        Idx++) {
        const Entry = DumpGdtEntry(CurrentEntry);
        logln(`[${Idx}]: ${Entry}`);
        CurrentEntry = CurrentEntry.add(Entry._Size);
    }
}

function DumpGdt(Selector) {
    const [GdtBase, _] = GetGdt();
    const Index = Selector.bitwiseShiftRight(3);
    const Offset = Index.multiply(8);
    const EntryAddress = GdtBase.add(Offset);
    const Entry = DumpGdtEntry(EntryAddress);
    logln(Entry);
}

function Gdt(Selector) {
    const Attributes = host.currentSession.Attributes;
    const IsKernel = Attributes.Target.IsKernelTarget;
    //
    // XXX: Not sure how to do this better?
    // Attributes.Machine.PointerSize is 4 when running in a Wow64 thread :-/.
    //
    let Is64Bit = true;
    try { host.createPointerObject(0, 'nt', '_KGDTENTRY64*'); } catch(e) { Is64Bit = false; }
    if (!IsKernel || !Is64Bit) {
        logln('The running session is not a kernel session or it is not running a 64-bit OS, so exiting');
        return;
    }

    if (Selector == undefined) {
        DumpAllGdt();
    } else {
        DumpGdt(Selector);
    }
}

function initializeScript() {
    return [
        new host.apiVersionSupport(1, 3),
        new host.functionAlias(
            Gdt,
            'gdt'
        ),
    ];
}


================================================
FILE: parse_eh_win64/README.md
================================================
# parse_eh_win64.js

`parse_eh_win64.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debugger-scripting) debugger extension for WinDbg that shows examples of how to extending the data-model with exception-handling related information for 64 bits executables.

More background is available in this article: [Debugger data model, Javascript & x64 exception handling](https://doar-e.github.io/blog/2017/12/01/debugger-data-model/).

## Usage

Run `.scriptload parse_eh_win64.js` to load the script. The script extends the `Debugger.Models.Process`, `Debugger.Models.Module` models and also exposes the `!ehhandlers` command.

## Examples

* At the process level, dumping `Function` objects and ordering them by the number of exception-handlers they define:

```text
0:002> dx @$curprocess.Functions.OrderByDescending(p => p.ExceptionHandlers.Count())
@$curprocess.Functions.OrderByDescending(p => p.ExceptionHandlers.Count())                
    [0x0]            : RVA:7fffb64bebf0 -> RVA:7fffb64bf022, 12 exception handlers
    [0x1]            : RVA:7fffb8bdff80 -> RVA:7fffb8be0b67, 11 exception handlers
    [0x2]            : RVA:7fffb1df8114 -> RVA:7fffb1df8360, 9 exception handlers
    [0x3]            : RVA:7fffa0111354 -> RVA:7fffa01115a0, 9 exception handlers
    [0x4]            : RVA:7fffb2183044 -> RVA:7fffb2183290, 9 exception handlers
    [0x5]            : RVA:7fffa0d41344 -> RVA:7fffa0d41590, 9 exception handlers
    [0x6]            : RVA:7fffb6573020 -> RVA:7fffb6573356, 6 exception handlers
    [0x7]            : RVA:7fffb4c71f94 -> RVA:7fffb4c720b4, 6 exception handlers
    [0x8]            : RVA:7fffb65e5774 -> RVA:7fffb65e5894, 6 exception handlers
    [0x9]            : RVA:7fffb660c62c -> RVA:7fffb660cf2e, 6 exception handlers
    [0xa]            : RVA:7fffb6c6f014 -> RVA:7fffb6c6f134, 6 exception handlers
    [0xb]            : RVA:7fffb8b9a350 -> RVA:7fffb8b9b39b, 6 exception handlers
    [0xc]            : RVA:7fffb35168a0 -> RVA:7fffb3516efb, 5 exception handlers
```

* Dumping a `Function` object:

```text
0:002> dx -r1 @$curprocess.Functions[0]
@$curprocess.Functions[0]                 : RVA:7ff67025a6d0 -> RVA:7ff67025a738, 1 exception handlers
    EHHandlerRVA     : 0x9b9700
    EHHandler        : 0x7ff6708f9700
    BeginRVA         : 0x31a6d0
    EndRVA           : 0x31a738
    Begin            : 0x7ff67025a6d0
    End              : 0x7ff67025a738
    ExceptionHandlers :   __try {7ff67025a6fb -> 7ff67025a712} __except(EXCEPTION_EXECUTE_HANDLER) {7ff67025a736}
```

* At the module level, dumping `ExceptionHandler` objects:

```text
0:002> dx @$curprocess.Modules[0].ExceptionHandlers
@$curprocess.Modules[0].ExceptionHandlers                 : Exception handlers
    [0x0]            :   __try {7ff67025a6fb -> 7ff67025a712} __except(EXCEPTION_EXECUTE_HANDLER) {7ff67025a736}
    [0x1]            :   __try {7ff6708f80b3 -> 7ff6708f813e} __except(7ff6708f93f2()) {7ff6708f813e}
    [0x2]            :   __try {7ff6708f90fd -> 7ff6708f9202} __except(7ff6708f9425()) {7ff6708f9202}
    [0x3]            :   __try {7ff6708f9236 -> 7ff
```

* Dumping an `ExceptionHandler` object:

```text
0:002> dx @$curprocess.Modules[0].ExceptionHandlers[0]
@$curprocess.Modules[0].ExceptionHandlers[0]                 :   __try {7ff67025a6fb -> 7ff67025a712} __except(EXCEPTION_EXECUTE_HANDLER) {7ff67025a736}
    Begin            : 0x7ff67025a6fb
    End              : 0x7ff67025a712
    HandlerAddress   : 0x1
    JumpTarget       : 0x7ff67025a736
    IsTryFinally     : false
    HasFilter        : false
```

* Dumping the current call-stack with EH information:

```text
0:002> !ehhandlers
5 stack frames, scanning for handlers...
Frame 1: EHHandler: 7fffb8c1fc90: ntdll!_C_specific_handler:
              Except: 7fffb8c5ef1d: ntdll!DbgUiRemoteBreakin+0x4d:
Frame 3: EHHandler: 7fffb8c1fc90: ntdll!_C_specific_handler:
              Except: 7fffb8bfa267: ntdll!RtlUserThreadStart+0x37:
              Filter: 7fffb8c38021: ntdll!RtlUserThreadStart$filt$0:
@$ehhandlers()  
```



================================================
FILE: parse_eh_win64/parse_eh_win64.js
================================================
// Axel '0vercl0k' Souchet - Dec 2017

"use strict";

let log = host.diagnostics.debugLog;
let logln = function (e) {
    host.diagnostics.debugLog(e + '\n');
};

function read_u32(addr) {
    return host.memory.readMemoryValues(addr, 1, 4)[0];
}

function read_u8(addr) {
    return host.memory.readMemoryValues(addr, 1, 1)[0];
}

class ScopeRecord {
    // 0:000> dt SCOPE_RECORD
    //   +0x000 BeginAddress     : Uint4B
    //   +0x004 EndAddress       : Uint4B
    //   +0x008 HandlerAddress   : Uint4B
    //   +0x00c JumpTarget       : Uint4B
    constructor(ScopeRecordAddress) {
        this.Begin = read_u32(ScopeRecordAddress);
        this.End = read_u32(ScopeRecordAddress.add(0x4));
        this.HandlerAddress = read_u32(ScopeRecordAddress.add(0x8));
        this.JumpTarget = read_u32(ScopeRecordAddress.add(0xc));
    }

    get IsTryFinally() {
        return this.JumpTarget.compareTo(0) == 0;
    }

    get HasFilter() {
        return this.IsTryFinally == false &&
            this.HandlerAddress.compareTo(1) != 0;
    }

    InBound(Begin, End) {
        return this.Begin.compareTo(Begin) >= 0 &&
            this.End.compareTo(End) < 0;
    }

    toString() {
        let S = '  __try {'
        S += this.Begin.toString(16) + ' -> ' + this.End.toString(16);
        S += '}';
        if (this.IsTryFinally == true) {
            S += ' __finally {';
            S += this.HandlerAddress.toString(16);
        } else {
            S += ' __except(';
            if (this.HasFilter == false) {
                S += 'EXCEPTION_EXECUTE_HANDLER';
            } else {
                S += this.HandlerAddress.toString(16) + '()';
            }
            S += ') {';
            S += this.JumpTarget.toString(16);
        }
        S += '}';
        return S;
    }
}

class Function {
    constructor(BaseAddress, EHHandler, Begin, End) {
        this.__BaseAddress = BaseAddress;
        this.EHHandlerRVA = EHHandler;
        this.EHHandler = BaseAddress.add(EHHandler);
        this.BeginRVA = Begin;
        this.EndRVA = End;
        this.Begin = BaseAddress.add(Begin);
        this.End = BaseAddress.add(End);
        this.ExceptionHandlers = [];
    }

    SetRecords(Records, KeepRVA = false) {
        this.ExceptionHandlers = Records;
        if (KeepRVA) {
            return;
        }

        for (let ExceptionHandler of this.ExceptionHandlers) {
            ExceptionHandler.Begin = ExceptionHandler.Begin.add(this.__BaseAddress);
            ExceptionHandler.End = ExceptionHandler.End.add(this.__BaseAddress);
            if (ExceptionHandler.JumpTarget.compareTo(0) != 0) {
                ExceptionHandler.JumpTarget = ExceptionHandler.JumpTarget.add(this.__BaseAddress);
            }
            if (ExceptionHandler.HandlerAddress.compareTo(1) != 0) {
                ExceptionHandler.HandlerAddress = ExceptionHandler.HandlerAddress.add(this.__BaseAddress);
            }
        }
    }

    toString() {
        let S = 'RVA:' + this.Begin.toString(16) + ' -> RVA:' + this.End.toString(16);
        S += ', ' + this.ExceptionHandlers.length + ' exception handlers';
        return S;
    }
}

function ParseCSpecificHandlerDatas(
    ScopeCount,
    ScopeRecords,
    Function
) {
    // 0:000> ?? sizeof(SCOPE_RECORD)
    // unsigned int64 0x10
    let Records = [];
    let ScopeSize = ScopeCount.multiply(0x10);
    for (let i = 0; i < ScopeSize; i += 0x10) {
        let CurrentScope = ScopeRecords.add(i);
        let Record = new ScopeRecord(CurrentScope);
        if (Record.InBound(Function.BeginRVA, Function.EndRVA) == false) {
            return [];
        }
        Records.push(Record);
    }
    return Records;
}

function ExtractExceptionHandlersForModule(
    BaseAddress,
    KeepRVA = false
) {
    let EHANDLER = 1;
    let IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3;

    // 0:000> dt _IMAGE_DOS_HEADER e_lfanew
    //   +0x03c e_lfanew : Int4B
    let NtHeaders = BaseAddress.add(read_u32(BaseAddress.add(0x3c)));

    // 0:000> dt _IMAGE_NT_HEADERS64 OptionalHeader
    //   +0x018 OptionalHeader : _IMAGE_OPTIONAL_HEADER64
    // 0:000> dt _IMAGE_OPTIONAL_HEADER64 DataDirectory
    //   +0x070 DataDirectory : [16] _IMAGE_DATA_DIRECTORY
    // 0:000> dt _IMAGE_DATA_DIRECTORY
    //   +0x000 VirtualAddress   : Uint4B
    //   +0x004 Size             : Uint4B
    let EntryExceptionDirectory = NtHeaders.add(0x18 + 0x70 + (IMAGE_DIRECTORY_ENTRY_EXCEPTION * 8));
    let RuntimeFunctionEntry = BaseAddress.add(read_u32(EntryExceptionDirectory));
    let SizeOfDirectory = read_u32(EntryExceptionDirectory.add(4));
    let Functions = [];

    for (let i = 0; i < SizeOfDirectory; i += 0xC) {
        // 0:000> dt _IMAGE_RUNTIME_FUNCTION_ENTRY
        //   +0x000 BeginAddress     : Uint4B
        //   +0x004 EndAddress       : Uint4B
        //   +0x008 UnwindInfoAddress : Uint4B
        //   +0x008 UnwindData       : Uint4B
        // 0:000> ?? sizeof(_IMAGE_RUNTIME_FUNCTION_ENTRY)
        // unsigned int64 0xc
        let CurrentEntry = RuntimeFunctionEntry.add(i);
        let BeginAddress = read_u32(CurrentEntry);
        let EndAddress = read_u32(CurrentEntry.add(4));

        if (BeginAddress.compareTo(0) == 0 || EndAddress.compareTo(0) == 0) {
            continue;
        }

        // 0:000> dt UNWIND_INFO
        //   +0x000 Version          : Pos 0, 3 Bits
        //   +0x000 Flags            : Pos 3, 5 Bits
        //   +0x001 SizeOfProlog     : UChar
        //   +0x002 CountOfCodes     : UChar
        //   +0x003 FrameRegister    : Pos 0, 4 Bits
        //   +0x003 FrameOffset      : Pos 4, 4 Bits
        //   +0x004 UnwindCode       : [1] UNWIND_CODE
        let UnwindInfo = BaseAddress.add(read_u32(CurrentEntry.add(8)));
        let UnwindInfoFlags = read_u8(UnwindInfo).bitwiseShiftRight(3);
        if (UnwindInfoFlags.bitwiseAnd(EHANDLER).compareTo(0) == 0) {
            continue;
        }

        // 0:000> ?? sizeof(UNWIND_CODE)
        // unsigned int64 2
        let CountOfCodes = read_u8(UnwindInfo.add(2));
        // For alignment purposes, this array will always have an even number of entries,
        // with the final entry potentially unused (in which case the array will be one
        // longer than indicated by the count of unwind codes field).
        let AlignedCountOfCodes = (CountOfCodes + 1) & ~1;

        // 0:000> dt UNWIND_INFO_END
        //   +0x000 ExceptionHandler : Uint4B
        //   +0x004 ExceptionData    : Uint4B
        let UnwindInfoEnd = UnwindInfo.add(4 + (AlignedCountOfCodes * 2));
        let ExceptionHandler = read_u32(UnwindInfoEnd);

        // 0:000> dt SEH_SCOPE_TABLE
        //   +0x000 Count            : Uint4B
        //   +0x004 ScopeRecord      : [1] SCOPE_RECORD
        let ScopeTable = UnwindInfoEnd.add(4);
        let ScopeCount = read_u32(ScopeTable);
        if (ScopeCount == 0) {
            continue;
        }

        let Records = ScopeTable.add(4);
        let CurrentFunction = new Function(
            BaseAddress, ExceptionHandler,
            BeginAddress, EndAddress
        );
        Records = ParseCSpecificHandlerDatas(ScopeCount, Records, CurrentFunction);
        if (Records.length == 0) {
            continue;
        }

        CurrentFunction.SetRecords(Records, KeepRVA);
        Functions.push(CurrentFunction);
    }

    return Functions;
}

class ModelExceptionHandlers {
    constructor(Type, Inst) {
        this.__module = null;
        this.__process = null;
        if (Type == 'Module') {
            this.__module = Inst;
        } else {
            this.__process = Inst;
        }
        this.__handlers = null;
        // We do not parse the exception handlers information here
        // as this constructor is called everytime you do:
        //  dx @$curprocess
        // so we will only parse the information when the user asked for it.
    }

    __ExceptionHandlers(Modules) {
        let Handlers = [];
        for (let Module of Modules) {
            let Functions = ExtractExceptionHandlersForModule(Module.BaseAddress);
            for (let Function of Functions) {
                Handlers = Handlers.concat(Function.ExceptionHandlers);
            }
        }
        return Handlers;
    }

    *[Symbol.iterator]() {
        if (this.__handlers == null) {
            // Only parse the infornmation once everytine we query it.
            let Modules = [this.__module];
            if (this.__process != null) {
                Modules = this.__process.Modules;
            }
            this.__handlers = this.__ExceptionHandlers(Modules);
        }

        for (let Handler of this.__handlers) {
            yield Handler;
        }
    }

    toString() {
        return 'Exception handlers';
    }
}

class ModelFunctions {
    constructor(Type, Inst) {
        this.__module = null;
        this.__process = null;
        if (Type == 'Module') {
            this.__module = Inst;
        } else {
            this.__process = Inst;
        }
        this.__functions = null;
    }

    __Functions(Modules) {
        let Functions = [];
        for (let Module of Modules) {
            let CurrentFunctions = ExtractExceptionHandlersForModule(Module.BaseAddress);
            Functions = Functions.concat(CurrentFunctions);
        }
        return Functions;
    }

    *[Symbol.iterator]() {
        if (this.__functions == null) {
            this.__functions = [];
            let Modules = [this.__module];
            if (this.__process != null) {
                Modules = this.__process.Modules;
            }

            this.__functions = this.__Functions(Modules);
        }

        for (let Function of this.__functions) {
            yield Function;
        }
    }

    toString() {
        return 'Functions';
    }
}

class __ProcessModelExtension {
    get ExceptionHandlers() {
        return new ModelExceptionHandlers('Process', this);
    }

    get Functions() {
        return new ModelFunctions('Process', this);
    }
}

class __ModuleModelExtension {
    get ExceptionHandlers() {
        return new ModelExceptionHandlers('Module', this);
    }

    get Functions() {
        return new ModelFunctions('Module', this);
    }
}

function BangEHHandlers() {
    let Control = host.namespace.Debugger.Utility.Control;
    let CurrentThread = host.currentThread;
    let CurrentProcess = host.currentProcess;
    let Registers = CurrentThread.Registers.User;

    let ReturnAddresses = [Registers.rip];
    let Frames = CurrentThread.Stack.Frames;
    for (let Frame of Frames) {
        ReturnAddresses.push(Frame.Attributes.ReturnOffset);
    }

    logln(ReturnAddresses.length + ' stack frames, scanning for handlers...');
    let Functions = Array.from(CurrentProcess.Functions);
    for (let Entry of ReturnAddresses.entries()) {
        let FrameNumber = host.Int64(Entry[0]);
        let ReturnAddress = Entry[1];
        let Func = Functions.find(
            c => ReturnAddress.compareTo(c.Begin) >= 0 &&
                ReturnAddress.compareTo(c.End) < 0
        );

        if (Func == undefined) {
            continue;
        }

        let ExceptionHandlers = Array.from(Func.ExceptionHandlers);
        let ExceptionHandler = ExceptionHandlers.find(
            c => ReturnAddress.compareTo(c.Begin) >= 0 &&
                ReturnAddress.compareTo(c.End) < 0
        )

        if (ExceptionHandler == undefined) {
            continue;
        }

        let Filter = undefined;
        let EHHandler = Func.EHHandler;
        let Handler = ExceptionHandler.HandlerAddress;
        let Name = 'Finally';
        if (ExceptionHandler.IsTryFinally == false) {
            if (ExceptionHandler.HasFilter) {
                Filter = ExceptionHandler.HandlerAddress;
            }
            Handler = ExceptionHandler.JumpTarget;
            Name = ' Except';
        }

        let FormatAddress = function (Handler) {
            let S = Handler.toString(16) + ': ';
            S += Control.ExecuteCommand(
                'u ' + Handler.toString(16) + ' l1'
            ).First();
            return S;
        }

        logln('Frame ' + FrameNumber.toString(16) + ': EHHandler: ' + FormatAddress(EHHandler));
        logln('             ' + Name + ': ' + FormatAddress(Handler));
        if (Filter != undefined) {
            logln('              Filter: ' + FormatAddress(Filter));
        }
    }
}

function initializeScript() {
    return [
        new host.namedModelParent(
            __ProcessModelExtension,
            'Debugger.Models.Process'
        ),
        new host.namedModelParent(
            __ModuleModelExtension,
            'Debugger.Models.Module'
        ),
        new host.functionAlias(
            BangEHHandlers,
            'ehhandlers'
        )
    ];
}


================================================
FILE: policybuffer/README.md
================================================
# policybuffer.js

`policybuffer.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debugger-scripting) debugger extension for WinDbg that disassembles Policy buffer programs used by the Chromium sandbox. Those programs are used to evaluate policy decision.

## Usage

Run `.scriptload policybuffer.js` to load the script. You can invoke the disassembler feature with `!disasspolicy <addr>`.

## Examples

* Dumping the File System policy of Firefox:

```text
0:017> !disasspolicy 0x00000131`d2142208
!OP_NUMBER_AND_MATCH<Param1, 0x1>
!OP_WSTRING_MATCH<Param0, "\??\", Length(0x4), Offset(0x0), CASE_SENSITIVE>
OP_ACTION<ASK_BROKER>

!OP_NUMBER_AND_MATCH<Param1, 0x1>
OP_WSTRING_MATCH<Param0, "~", Length(0x1), Offset(0xffffffff), CASE_SENSITIVE>
OP_ACTION<ASK_BROKER>

OP_WSTRING_MATCH<Param0, "\??\C:\Users\over\AppData\LocalLow\Mozilla\Temp-{8aca3358-7266-42d0-a521-805394768d86}\", Length(0x57), Offset(0x0), CASE_INSENSITIVE>
OP_ACTION<ASK_BROKER>

!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>
OP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>
OP_WSTRING_MATCH<Param0, "\??\C:\Users\over\AppData\Local\Microsoft\Windows\Fonts\", Length(0x38), Offset(0x0), CASE_INSENSITIVE>
OP_ACTION<ASK_BROKER>

!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>
OP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>
OP_WSTRING_MATCH<Param0, "\??\C:\work\firefox-66.0a1.en-US.win64\firefox\", Length(0x2f), Offset(0x0), CASE_INSENSITIVE>
OP_ACTION<ASK_BROKER>

!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>
OP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>
OP_WSTRING_MATCH<Param0, "\??\C:\Users\over\AppData\Roaming\Mozilla\Firefox\Profiles\rbo6kdsb.default-nightly\chrome\", Length(0x5b), Offset(0x0), CASE_INSENSITIVE>
OP_ACTION<ASK_BROKER>

!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>
OP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>
OP_WSTRING_MATCH<Param0, "\??\C:\Users\over\AppData\Roaming\Mozilla\Firefox\Profiles\rbo6kdsb.default-nightly\extensions\", Length(0x5f), Offset(0x0), CASE_INSENSITIVE>
OP_ACTION<ASK_BROKER>

!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>
OP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>
OP_WSTRING_MATCH<Param0, "\??\C:\Users\over\AppData\Roaming\Mozilla\SystemExtensionsDev\", Length(0x3e), Offset(0x0), CASE_INSENSITIVE>
OP_ACTION<ASK_BROKER>

!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>
OP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>
OP_WSTRING_MATCH<Param0, "\??\C:\Users\over\AppData\Roaming\Mozilla\Extensions\", Length(0x35), Offset(0x0), CASE_INSENSITIVE>
OP_ACTION<ASK_BROKER>

OP_WSTRING_MATCH<Param0, "\??\pipe\chrome.", Length(0x10), Offset(0x0), CASE_INSENSITIVE>
OP_ACTION<ASK_BROKER>

OP_WSTRING_MATCH<Param0, "\??\pipe\gecko-crash-server-pipe.", Length(0x21), Offset(0x0), CASE_INSENSITIVE>
OP_ACTION<ASK_BROKER>

@$disasspolicy(0x00000131`d2142208)
```


================================================
FILE: policybuffer/policybuffer.js
================================================
// Axel '0vercl0k' Souchet - 5 March 2019
// sandbox::PolicyBase::EvalPolicy

'use strict';

//
// Utility functions.
//

const Log = host.diagnostics.debugLog;
const Logln = p => host.diagnostics.debugLog(p + '\n');
const Hex = p => '0x' + p.toString(16);
const ReadWstring = p => host.memory.readWideString(p);

function ReadShort(Address) {
    let Value = null;
    try {
        Value = host.memory.readMemoryValues(
           Address, 1, 2
        )[0];
    } catch(e) {
    }

    return Value;
}

function ReadDword(Address) {
    let Value = null;
    try {
        Value = host.memory.readMemoryValues(
           Address, 1, 4
        )[0];
    } catch(e) {
    }

    return Value;
}

function ReadQword(Address) {
    let Value = null;
    try {
        Value = host.memory.readMemoryValues(
           Address, 1, 8
        )[0];
    } catch(e) {
    }

    return Value;
}

function initializeScript() {
    return [
        new host.apiVersionSupport(1, 3)
    ];
}

//
// Constants.
//

// The low-level policy is implemented using the concept of policy 'opcodes'.
// An opcode is a structure that contains enough information to perform one
// comparison against one single input parameter. For example, an opcode can
// encode just one of the following comparison:
//
// - Is input parameter 3 not equal to NULL?
// - Does input parameter 2 start with L"c:\\"?
// - Is input parameter 5, bit 3 is equal 1?
//
// Each opcode is in fact equivalent to a function invocation where all
// the parameters are known by the opcode except one. So say you have a
// function of this form:
//      bool fn(a, b, c, d)  with 4 arguments
//
// Then an opcode is:
//      op(fn, b, c, d)
// Which stores the function to call and its 3 last arguments
//
// Then and opcode evaluation is:
//      op.eval(a)  ------------------------> fn(a,b,c,d)
//                        internally calls
//
// The idea is that complex policy rules can be split into streams of
// opcodes which are evaluated in sequence. The evaluation is done in
// groups of opcodes that have N comparison opcodes plus 1 action opcode:
//
// [comparison 1][comparison 2]...[comparison N][action][comparison 1]...
//    ----- evaluation order----------->
//
// Each opcode group encodes one high-level policy rule. The rule applies
// only if all the conditions on the group evaluate to true. The action
// opcode contains the policy outcome for that particular rule.

// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h#77
// The following are the implemented opcodes.
// enum OpcodeID {
// OP_ALWAYS_FALSE,  // Evaluates to false (EVAL_FALSE).
// OP_ALWAYS_TRUE,  // Evaluates to true (EVAL_TRUE).
// OP_NUMBER_MATCH,  // Match a 32-bit integer as n == a.
// OP_NUMBER_MATCH_RANGE,  // Match a 32-bit integer as a <= n <= b.
// OP_NUMBER_AND_MATCH,  // Match using bitwise AND; as in: n & a != 0.
// OP_WSTRING_MATCH,  // Match a string for equality.
// OP_ACTION  // Evaluates to an action opcode.
// };

const OP_ALWAYS_FALSE = 0;
const OP_ALWAYS_TRUE = 1;
const OP_NUMBER_MATCH = 2;
const OP_NUMBER_MATCH_RANGE = 3;
const OP_NUMBER_AND_MATCH = 4;
const OP_WSTRING_MATCH = 5;
const OP_ACTION = 6;

const Opcodes = {
    [OP_ALWAYS_FALSE] : 'OP_ALWAYS_FALSE',
    [OP_ALWAYS_TRUE] : 'OP_ALWAYS_TRUE',
    [OP_NUMBER_MATCH] : 'OP_NUMBER_MATCH',
    [OP_NUMBER_MATCH_RANGE] : 'OP_NUMBER_MATCH_RANGE',
    [OP_NUMBER_AND_MATCH] : 'OP_NUMBER_AND_MATCH',
    [OP_WSTRING_MATCH] : 'OP_WSTRING_MATCH',
    [OP_ACTION] : 'OP_ACTION'
};

// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h
// enum StringMatchOptions {
//  CASE_SENSITIVE = 0,      // Pay or Not attention to the case as defined by
//  CASE_INSENSITIVE = 1,    // RtlCompareUnicodeString windows API.
//  EXACT_LENGHT = 2         // Don't do substring match. Do full string match.
// };

const MatchingOptions = {
    0 : 'CASE_SENSITIVE',
    1 : 'CASE_INSENSITIVE',
    2 : 'EXACT_LENGTH',
    3 : 'EXACT_LENGTH | CASE_INSENSITIVE'
};

// These are the possible policy outcomes. Note that some of them might
// not apply and can be removed. Also note that The following values only
// specify what to do, not how to do it and it is acceptable given specific
// cases to ignore the policy outcome.
// enum EvalResult {
//   // Comparison opcode values:
//   EVAL_TRUE,   // Opcode condition evaluated true.
//   EVAL_FALSE,  // Opcode condition evaluated false.
//   EVAL_ERROR,  // Opcode condition generated an error while evaluating.
//   // Action opcode values:
//   ASK_BROKER,  // The target must generate an IPC to the broker. On the broker
//                // side, this means grant access to the resource.
//   DENY_ACCESS,   // No access granted to the resource.
//   GIVE_READONLY,  // Give readonly access to the resource.
//   GIVE_ALLACCESS,  // Give full access to the resource.
//   GIVE_CACHED,  // IPC is not required. Target can return a cached handle.
//   GIVE_FIRST,  // TODO(cpu)
//   SIGNAL_ALARM,  // Unusual activity. Generate an alarm.
//   FAKE_SUCCESS,  // Do not call original function. Just return 'success'.
//   FAKE_ACCESS_DENIED,  // Do not call original function. Just return 'denied'
//                        // and do not do IPC.
//   TERMINATE_PROCESS,  // Destroy target process. Do IPC as well.
// };

const Actions = {
    3 : 'ASK_BROKER',
    4 : 'DENY_ACCESS',
    5 : 'GIVE_READONLY',
    6 : 'GIVE_ALLACCESS',
    7 : 'GIVE_CACHED',
    8 : 'GIVE_FIRST',
    9 : 'SIGNAL_ALARM',
    10 : 'FAKE_SUCCESS',
    11 : 'FACE_ACCESS_DENIED',
    12 : 'TERMINATE_PROCESS'
};

// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/internal_types.h#19
// enum ArgType {
//   INVALID_TYPE = 0,
//   WCHAR_TYPE,
//   UINT32_TYPE,
//   UNISTR_TYPE,
//   VOIDPTR_TYPE,
//   INPTR_TYPE,
//   INOUTPTR_TYPE,
//   LAST_TYPE
// };

const ArgTypes = {
    0 : 'INVALID_TYPE',
    1 : 'WCHAR_TYPE',
    2 : 'UINT32_TYPE',
    3 : 'UNISTR_TYPE',
    4 : 'VOIDPTR_TYPE',
    5 : 'INPTR_TYPE',
    6 : 'INOUTPTR_TYPE',
    7 : 'LAST_TYPE'
};

// Options that apply to every opcode. They are specified when creating
// each opcode using OpcodeFactory::MakeOpXXXXX() family of functions
// Do nothing special.
const kPolNone = 0;

// Convert EVAL_TRUE into EVAL_FALSE and vice-versa. This allows to express
// negated conditions such as if ( a && !b).
const kPolNegateEval = 1;

// Zero the MatchContext context structure. This happens after the opcode
// is evaluated.
const kPolClearContext = 2;

// Use OR when evaluating this set of opcodes. The policy evaluator by default
// uses AND when evaluating. Very helpful when
// used with kPolNegateEval. For example if you have a condition best expressed
// as if(! (a && b && c)), the use of this flags allows it to be expressed as
// if ((!a) || (!b) || (!c)).
const kPolUseOREval = 4;

// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/policy_params.h#36
const ParameterNames = {
    'OpenFile' : [
        'NAME', 'BROKER', 'ACCESS', 'DISPOSITION', 'OPTIONS'
    ],
};

//
// Code.
//

function DisassPolicyBuffer(PolicyBufferAddress, PolicyType) {
    let Ptr = PolicyBufferAddress;
    const PolicyBufferOpcodeCount = ReadQword(Ptr);
    Ptr += 8;
    for(let Idx = 0; Idx < PolicyBufferOpcodeCount; ++Idx) {

        //
        // Save off the current pointer as it is useful to compute
        // where the stored string is in memory for the OP_WSTRING_MATCH
        // opcode.
        //

        const OpcodePtr = Ptr;

        //
        // Unpack the opcode structure.
        //

        const OpcodeId = ReadDword(Ptr);
        Ptr += 4;
        const SelectedParameter = ReadShort(Ptr);
        Ptr += 2;
        const Options = ReadShort(Ptr);
        Ptr += 2;
        const Parameters = [];
        for(let InnerIdx = 0; InnerIdx < 4; ++InnerIdx) {
            Parameters.push(ReadQword(Ptr));
            Ptr += 8;
        }

        //
        // Once we dumped the opcode, let's prettify its parameters.
        //

        const Operands = [];
        let FirstOperand = 'Param' + SelectedParameter;
        if(ParameterNames[PolicyType] != undefined) {
            FirstOperand = PolicyType + '::' + ParameterNames[PolicyType][SelectedParameter];
        }

        Operands.push(FirstOperand);
        if(OpcodeId == OP_ALWAYS_TRUE || OpcodeId == OP_ALWAYS_FALSE) {
        } else if(OpcodeId == OP_NUMBER_MATCH) {
            const ArgType = ArgTypes[Parameters[1].asNumber()];
            Operands.push(ArgType + '(' + Hex(Parameters[0]) + ')');
        } else if(OpcodeId == OP_NUMBER_MATCH_RANGE) {
            Operands.push('LowerBound(' + Hex(Parameters[0]) + ')');
            Operands.push('UpperBound(' + Hex(Parameters[1]) + ')');
        } else if(OpcodeId == OP_NUMBER_AND_MATCH) {
            Operands.push(Hex(Parameters[0]));
        }else if(OpcodeId == OP_WSTRING_MATCH) {
            const Displacement = Parameters[0];
            const StringAddress = OpcodePtr.add(Displacement);
            Operands.push('"' + ReadWstring(StringAddress) + '"');
            Operands.push('Length(' + Hex(Parameters[1]) + ')');
            Operands.push('Offset(' + Hex(Parameters[2]) + ')');
            const MatchingOption = Parameters[3].asNumber();
            Operands.push(MatchingOptions[MatchingOption]);
        } else if(OpcodeId == OP_ACTION) {

            //
            // The OP_ACTION is the only opcode that does not need a selected
            // parameter.
            //

            const Action = Actions[Parameters[0].asNumber()];
            Operands[0] = Action;
        }

        //
        // Display the opcode and its operands.
        //

        const OpcodeIdStr = Opcodes[OpcodeId];
        if(Options.bitwiseAnd(kPolNegateEval).compareTo(0) != 0) {
            Logln('!' + OpcodeIdStr + '<' + Operands.join(', ')  + '>');
        } else {
            Logln(OpcodeIdStr + '<' + Operands.join(', ')  + '>');
        }

        if(OpcodeId == OP_ACTION) {
            Logln('');
        }
    }
}

function initializeScript() {
    return [
        new host.apiVersionSupport(1, 3),
        new host.functionAlias(
            DisassPolicyBuffer,
            'disasspolicy'
        )
    ];
}



================================================
FILE: sm/README.md
================================================
# sm.js

`sm.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debugger-scripting) debugger extension for WinDbg that allows to dump both `js::Value` and `JSObject` [Spidermonkey](https://github.com/mozilla/gecko-dev/tree/master/js) objects. It works on crash-dumps, live debugging, and TTD traces.

The extension detects automatically if it is running from the [Javascript shell](https://github.com/mozilla/gecko-dev/tree/master/js/src/shell) (in which case `js.exe` is the module hosting the JavaScript engine code) or from Firefox directly (in which case `xul.dll` is the module hosting the JavaScript engine code). Private symbol information for the module hosting the JavaScript engine code is required. It only supports x64 version of spidermonkey.

It has been used and tested against spidermonkey during end of 2018 - but should work fine on newer versions assuming core data-structures haven't changed a whole lot (and if they do, it should be fairly easy to adapt anyway).

## Usage

Run `.scriptload sm.js` to load the script. You can dump `js::Value` with `!smdump_jsvalue` and `JSObject` with `!smdump_jsobject`. You can insert a software breakpoint in a JIT buffer with `!ion_insertbp` and `!in_nursery` to figure out if an object lives inside the Nursery heap.

## Examples

* Dumping the `js::Value` associated to the following JavaScript object `['short', 13.37, new Map([[ 1, 'one' ],[ 2, 'two' ]]), ['loooooooooooooooooooooooooooooong', [0x1337, {doare:'in d4 place'}]], false, null, undefined, true, Math.atan2, Math]`:

```text
0:000> !smdump_jsvalue vp[2].asBits_
1e5f10024c0: js!js::ArrayObject:   Length: 10
1e5f10024c0: js!js::ArrayObject: Capacity: 10
1e5f10024c0: js!js::ArrayObject:  Content: ['short', 13.37, new Map(...), ['loooooooooooooooooooooooooooooong', [0x1337, {'doare' : 'in d4 place'}]], false, null, undefined, true, atan2(), Math]
@$smdump_jsvalue(vp[2].asBits_)
```

* Setting a breakpoint in code being JIT'd by IonMonkey:

```text
0:008> g
Breakpoint 0 hit
js!js::jit::CodeGenerator::visitBoundsCheck:
00007ff7`87d9e1a0 4156            push    r14

0:000> !ion_insertbp
unsigned char 0xcc ''
unsigned int64 0x5b
@$ion_insertbp()

0:000> g
(1a60.f58): Break instruction exception - code 80000003 (first chance)
000003d9`ca67991b cc              int     3

0:000> u . l2
000003d9`ca67991b cc              int     3
000003d9`ca67991c 3bd8            cmp     ebx,eax
```

* Figure out if an object lives in the Nursery heap:

```text
0:008> !in_nursery 0x19767e00df8
Using previously cached JSContext @0x000001fe17318000
0x000001fe1731cde8: js::Nursery
 ChunkCountLimit: 0x0000000000000010 (16 MB)
        Capacity: 0x0000000000fffe80 bytes
    CurrentChunk: 0x0000019767e00000
        Position: 0x0000019767e00eb0
          Chunks:
            00: [0x0000019767e00000 - 0x0000019767efffff]
            01: [0x00001fa2aee00000 - 0x00001fa2aeefffff]
            02: [0x0000115905000000 - 0x00001159050fffff]
            03: [0x00002fc505200000 - 0x00002fc5052fffff]
            04: [0x000020d078700000 - 0x000020d0787fffff]
            05: [0x0000238217200000 - 0x00002382172fffff]
            06: [0x00003ff041f00000 - 0x00003ff041ffffff]
            07: [0x00001a5458700000 - 0x00001a54587fffff]
-------
0x19767e00df8 has been found in the js::NurseryChunk @0x19767e00000!

0:008> !in_nursery 0x00001fe174be810
Using previously cached JSContext @0x000001fe17318000
0x000001fe1731cde8: js::Nursery
 ChunkCountLimit: 0x0000000000000010 (16 MB)
        Capacity: 0x0000000000fffe80 bytes
    CurrentChunk: 0x0000019767e00000
        Position: 0x0000019767e00eb0
          Chunks:
            00: [0x0000019767e00000 - 0x0000019767efffff]
            01: [0x00001fa2aee00000 - 0x00001fa2aeefffff]
            02: [0x0000115905000000 - 0x00001159050fffff]
            03: [0x00002fc505200000 - 0x00002fc5052fffff]
            04: [0x000020d078700000 - 0x000020d0787fffff]
            05: [0x0000238217200000 - 0x00002382172fffff]
            06: [0x00003ff041f00000 - 0x00003ff041ffffff]
            07: [0x00001a5458700000 - 0x00001a54587fffff]
-------
0x1fe174be810 hasn't been found be in any Nursery js::NurseryChunk.
```


================================================
FILE: sm/sm.js
================================================
// Axel '0vercl0k' Souchet - 24-June-2018
//
// Example:
//   * from the interpreter:
//       Math.atan2(['short', 13.37, new Map([[ 1, 'one' ],[ 2, 'two' ]]), ['loooooooooooooooooooooooooooooong', [0x1337, {doare:'in d4 place'}]], false, null, undefined, true, Math.atan2, Math])
//
//   * from the debugger:
//       js!js::math_atan2:
//       00007ff6`0227e140 56              push    rsi
//       0:000> !smdump_jsvalue vp[2].asBits_
//       1e5f10024c0: js!js::ArrayObject:   Length: 10
//       1e5f10024c0: js!js::ArrayObject: Capacity: 10
//       1e5f10024c0: js!js::ArrayObject:  Content: ['short', 13.37, new Map(...), ['loooooooooooooooooooooooooooooong', [0x1337, {'doare' : 'in d4 place'}]], false, null, undefined, true, atan2(), Math]
//       @$smdump_jsvalue(vp[2].asBits_)
//

'use strict';

let Module = null;

const logln = p => host.diagnostics.debugLog(p + '\n');
const hex = p => '0x' + p.toString(16).padStart(16, '0');
const JSVAL_TAG_SHIFT = host.Int64(47);
const JSVAL_PAYLOAD_MASK = host.Int64(1).bitwiseShiftLeft(JSVAL_TAG_SHIFT).subtract(1);
const CLASS_NON_NATIVE = host.Int64(0x40000);
const FLAG_DELEGATE = host.Int64(8);

const JSVAL_TYPE_DOUBLE = host.Int64(0x1fff0);
const JSVAL_TYPE_INT32 = host.Int64(0x1fff1);
const JSVAL_TYPE_BOOLEAN = host.Int64(0x1fff2);
const JSVAL_TYPE_UNDEFINED = host.Int64(0x1fff3);
const JSVAL_TYPE_NULL = host.Int64(0x1fff4);
const JSVAL_TYPE_MAGIC = host.Int64(0x1fff5);
const JSVAL_TYPE_STRING = host.Int64(0x1fff6);
const JSVAL_TYPE_SYMBOL = host.Int64(0x1fff7);
const JSVAL_TYPE_OBJECT = host.Int64(0x1fffc);

const INLINE_CHARS_BIT = host.Int64(1 << 6);
const LATIN1_CHARS_BIT = host.Int64(1 << 9);

const JSID_TYPE_MASK = host.Int64(0x7);
const JSID_TYPE_STRING = host.Int64(0x0);
const JSID_TYPE_INT = host.Int64(0x1);
const JSID_TYPE_VOID = host.Int64(0x2);
const JSID_TYPE_SYMBOL = host.Int64(0x4);

const SLOT_MASK = host.Int64(0xffffff);
const FIXED_SLOTS_SHIFT = host.Int64(24);

const FunctionConstants = {
    0x0001 : 'INTERPRETED',
    0x0004 : 'EXTENDED',
    0x0008 : 'BOUND_FUN',
    0x0010 : 'WASM_OPTIMIZED',
    0x0020 : 'HAS_GUESSED_ATOM/HAS_BOUND_FUNCTION_NAME_PREFIX',
    0x0040 : 'LAMBDA',
    0x0080 : 'SELF_HOSTED',
    0x0100 : 'HAS_INFERRED_NAME',
    0x0200 : 'INTERPRETED_LAZY',
    0x0400 : 'RESOLVED_LENGTH',
    0x0800 : 'RESOLVED_NAME',
};

const FunctionKindConstants = {
    0 : 'NORMAL_KIND',
    1 : 'ARROW_KIND',
    2 : 'METHOD_KIND',
    3 : 'CLASSCONSTRUCTOR_KIND',
    4 : 'GETTER_KIND',
    5 : 'SETTER_KIND',
    6 : 'ASMJS_KIND'
};

const Tag2Names = {
    [JSVAL_TYPE_DOUBLE] : 'Double',
    [JSVAL_TYPE_INT32] : 'Int32',
    [JSVAL_TYPE_STRING] : 'String',
    [JSVAL_TYPE_UNDEFINED] : 'Undefined',
    [JSVAL_TYPE_BOOLEAN] : 'Boolean',
    [JSVAL_TYPE_NULL] : 'Null',
    [JSVAL_TYPE_OBJECT] : 'Object',
    [JSVAL_TYPE_SYMBOL] : 'Symbol',
    [JSVAL_TYPE_MAGIC] : 'Magic',
};

//
// Read a uint64_t integer from Addr.
//

function read_u64(Addr) {
    return host.memory.readMemoryValues(Addr, 1, 8)[0];
}

//
// Mirror the functionality of ::fromElements.
//

function heapslot_to_objectelements(Addr) {
    // static ObjectElements* fromElements(HeapSlot* elems) {
    //  return reinterpret_cast<ObjectElements*>(uintptr_t(elems) - sizeof(ObjectElements));
    // }
    const ObjectElementsSize = host.getModuleType(Module, 'js::ObjectElements').size;
    const ObjectElements = host.createPointerObject(
        Addr.subtract(ObjectElementsSize),
        Module,
        'js::ObjectElements*'
    );

    return ObjectElements;
}

//
// Is Byte printable?
//

function printable(Byte) {
    return Byte >= 0x20 && Byte <= 0x7e;
}

//
// Return a string describing Byte; either a \x41 or its ascii representation
//

function byte_to_str(Byte) {
    if(printable(Byte)) {
        return String.fromCharCode(Byte);
    }

    return '\\x' + Byte.toString(16).padStart(2, '0');
}

//
// Is this jsid an integer?
//

function jsid_is_int(Propid) {
    const Bits = Propid.value.asBits;
    return Bits.bitwiseAnd(JSID_TYPE_MASK).compareTo(JSID_TYPE_INT) == 0;
}

//
// Is this jsid a string?
//

function jsid_is_string(Propid) {
    const Bits = Propid.value.asBits;
    return Bits.bitwiseAnd(JSID_TYPE_MASK).compareTo(JSID_TYPE_STRING) == 0;
}

//
// Retrieve a property from a Shape; returns an actual integer/string based
// on the propid_.
//

function get_property_from_shape(Shape) {
    // XXX: expose a smdump_jsid
    const Propid = Shape.propid_;
    if(jsid_is_int(Propid)) {
        return Propid.value.asBits.bitwiseShiftRight(1);
    }

    if(jsid_is_string(Propid)) {
        return new __JSString(Propid.value.asBits);
    }

    // XXX: todo
}

function jsvalue_to_instance(Addr) {
    const JSValue = new __JSValue(Addr);
    if(!Tag2Names.hasOwnProperty(JSValue.Tag)) {
        return 'Dunno';
    }

    const Name = Tag2Names[JSValue.Tag];
    const Type = Names2Types[Name];
    return new Type(JSValue.Payload);
}

class __JSMagic {
    constructor(Addr) {
        this._Addr = Addr;
    }

    toString() {
        return 'magic';
    }

    Logger(Content) {
        logln(this._Addr.toString(16) + ': JSVAL_TYPE_MAGIC: ' + Content);
    }

    Display() {
        this.Logger(this);
    }
}

class __JSArgument {
    //  * ArgumentsObject instances use the following reserved slots:
    //  *
    //  *   INITIAL_LENGTH_SLOT
    //  *     Stores the initial value of arguments.length, plus a bit indicating
    //  *     whether arguments.length and/or arguments[@@iterator] have been
    //  *     modified.  Use initialLength(), hasOverriddenLength(), and
    //  *     hasOverriddenIterator() to access these values.  If arguments.length has
    //  *     been modified, then the current value of arguments.length is stored in
    //  *     another slot associated with a new property.
    //  *   DATA_SLOT
    //  *     Stores an ArgumentsData*, described above.
    //  *   MAYBE_CALL_SLOT
    //  *     Stores the CallObject, if the callee has aliased bindings. See
    //  *     the ArgumentsData::args comment.
    //  *   CALLEE_SLOT
    //  *     Stores the initial arguments.callee. This value can be overridden on
    //  *     mapped arguments objects, see hasOverriddenCallee.
    //  */
    // class ArgumentsObject : public NativeObject {
    //     protected:
    //      static const uint32_t INITIAL_LENGTH_SLOT = 0;
    //      static const uint32_t DATA_SLOT = 1;
    //      static const uint32_t MAYBE_CALL_SLOT = 2;
    //      static const uint32_t CALLEE_SLOT = 3;
    constructor(Addr) {
        this._Addr = Addr;
        this._Obj = host.createPointerObject(
            Addr,
            Module,
            'js::ArgumentsObject*'
        );

        const INITIAL_LENGTH_SLOT = host.Int64(0);
        const DATA_SLOT = host.Int64(1);
        const MAYBE_CALL_SLOT = host.Int64(2);
        const CALLEE_SLOT = host.Int64(3);
        const ArgumentsObjectSize = this._Obj.dereference().targetType.size;
        this._SlotAddress = this._Obj.address.add(ArgumentsObjectSize);

        const InitialLengthSlot = read_u64(this._SlotAddress.add(
            INITIAL_LENGTH_SLOT.multiply(8)
        ));
        this._InitialLength = new __JSInt32(InitialLengthSlot)._Value;

        this._Data = read_u64(this._SlotAddress.add(
            DATA_SLOT.multiply(8)
        )).bitwiseShiftLeft(1);
    }

    toString() {
        return 'Arguments(..)';
    }

    Logger(Content) {
        logln(this._Addr.toString(16) + ': js!js::ArgumentsObject: ' + Content);
    }

    Display() {
        this.Logger('InitialLength: ' + this._InitialLength);
        this.Logger('         Data: ' + hex(this._Data) + ' (js!js::ArgumentsData)');
    }
}

class __JSNull {
    constructor(Addr) {
        this._Addr = Addr;
    }

    toString() {
        return 'null';
    }

    Logger(Content) {
        logln(this._Addr.toString(16) + ': JSVAL_TYPE_NULL: ' + Content);
    }

    Display() {
        this.Logger(this);
    }
}

class __JSUndefined {
    constructor(Addr) {
        this._Addr = Addr;
    }

    toString() {
        return 'undefined';
    }

    Logger(Content) {
        logln(this.Addr.toString(16) + ': JSVAL_TYPE_UNDEFINED: ' + Content);
    }

    Display() {
        this.Logger(this);
    }
}

class __JSBoolean {
    constructor(Addr) {
        this._Addr = Addr;
        this._Value = Addr.compareTo(1) == 0 ? true : false;
    }

    toString() {
        return this._Value.toString();
    }

    Logger(Content) {
        logln(this._Addr.toString(16) + ': JSVAL_TYPE_BOOLEAN: ' + Content);
    }

    Display() {
        this.Logger(this);
    }
}

class __JSInt32 {
    constructor(Addr) {
        this._Addr = Addr;
        this._Value = Addr.bitwiseAnd(0xffffffff);
    }

    toString() {
        return '0x' + this._Value.toString(16);
    }

    Logger(Content) {
        logln(this._Addr.toString(16) + ': JSVAL_TYPE_INT32: ' + Content);
    }

    Display() {
        this.Logger(this);
    }
}

class __JSString {
    constructor(Addr) {
        this._Obj = host.createPointerObject(
            Addr,
            Module,
            'JSString*'
        );
        /*
         * The Flags Word
         *
         * The flags word stores both the string's type and its character encoding.
         *
         * If LATIN1_CHARS_BIT is set, the string's characters are stored as Latin1
         * instead of TwoByte. This flag can also be set for ropes, if both the
         * left and right nodes are Latin1. Flattening will result in a Latin1
         * string in this case.
         */
        const Flags = this._Obj.d.flags_;
        const IsLatin1 = Flags.bitwiseAnd(LATIN1_CHARS_BIT).compareTo(0) != 0;
        const IsInline = Flags.bitwiseAnd(INLINE_CHARS_BIT).compareTo(0) != 0;
        let Address = null;
        if(IsInline) {

            //
            // inlineStorageLatin1 and inlineStorageTwoByte are in a union and
            // as a result are at the same address
            //

            Address = this._Obj.d.inlineStorageLatin1.address;
        } else {

            //
            // Same as above with nonInlineStorageLatin1 and nonInlineStorageTwoByte.
            //

            Address = this._Obj.d.s.u2.nonInlineCharsLatin1.address;
        }

        let Length = Flags.bitwiseShiftRight(32);
        if(!IsLatin1) {
            Length *= 2;
        }

        this._String = Array.from(host.memory.readMemoryValues(
            Address,
            Length,
            1
        )).map(
            p => byte_to_str(p)
        ).join('');
    }

    toString() {
        return "'" + this._String + "'";
    }

    Logger(Content) {
        logln(this._Obj.address.toString(16) + ': js!JSString: ' + Content);
    }

    Display() {
        this.Logger(this);
    }
}

class __JSValue {
    constructor(Addr) {
        this._Addr = Addr;
        this._Tag = this._Addr.bitwiseShiftRight(JSVAL_TAG_SHIFT);
        this._IsDouble = this._Tag.compareTo(JSVAL_TYPE_DOUBLE) < 0;
        this._Payload = this._Addr.bitwiseAnd(JSVAL_PAYLOAD_MASK);
    }

    get Payload() {
        if(this._IsDouble) {
            return this._Addr;
        }

        return this._Payload;
    }

    get Tag() {
        if(this._IsDouble) {
            return JSVAL_TYPE_DOUBLE;
        }

        return this._Tag;
    }
}

class __JSArray {
    constructor(Addr) {
        this._Obj = host.createPointerObject(
            Addr,
            Module,
            'js::ArrayObject*'
        );
        // XXX: why doesn't it work?
        // this.Obj.elements_.value.address
        this._Content = this._Obj.elements_.address;
        this._Header = heapslot_to_objectelements(this._Content);
        // The flags word stores both the flags and the number of shifted elements.
        // Allow shifting 2047 elements before actually moving the elements.
        const NumShiftedElementsBits = host.Int64(11);
        const NumShiftedElementsShift = host.Int64(32).subtract(NumShiftedElementsBits);
        this._Flags = this._Header.flags;
        this._NumShifted = this._Flags.bitwiseShiftRight(NumShiftedElementsShift)
        this._Length = this._Header.length;
        this._Capacity = this._Header.capacity;
        this._InitializedLength = this._Header.initializedLength;
    }

    toString() {
        const Max = 10;
        const Content = [];
        for(let Idx = 0; Idx < Math.min(Max, this._InitializedLength); ++Idx) {
            const Addr = this._Content.add(Idx * 8);
            const JSValue = read_u64(Addr);
            const Inst = jsvalue_to_instance(JSValue);
            Content.push(Inst.toString());
        }

        return '[' + Content.join(', ') + (this._Length > Max ? ', ...' : '') + ']';
    }

    Logger(Content) {
        logln(this._Obj.address.toString(16) + ': js!js::ArrayObject: ' + Content);
    }

    Display() {
        this.Logger('           Length: ' + this._Length);
        this.Logger('         Capacity: ' + this._Capacity);
        this.Logger('InitializedLength: ' + this._InitializedLength);
        this.Logger('       NumShifted: ' + this._NumShifted + ' (flags: ' + hex(this._Flags) + ')');
        this.Logger('          Content: ' + this);
    }
}

class __JSFunction {
    constructor(Addr) {
        this._Obj = host.createPointerObject(
            Addr,
            Module,
            'JSFunction*'
        );

        this._Atom = this._Obj.atom_.value.address;
        this._Name = '<anonymous>';
        if(this._Atom.compareTo(0) != 0) {
            this._Name = new __JSString(this._Atom).toString().slice(1, -1);
        }

        this._Name += '()';
        this._Flags = this._Obj.flags_;
    }

    toString() {
        return this._Name;
    }

    get Flags() {
        const S = [];
        for(const Key in FunctionConstants) {
            if(this._Flags.bitwiseAnd(host.parseInt64(Key)).compareTo(0) != 0) {
                S.push(FunctionConstants[Key]);
            }
        }

        const Kind = (this._Flags >> 13) & 7;
        S.push(FunctionKindConstants[Kind]);
        return S.join(' | ');
    }

    Logger(Content) {
        logln(this._Obj.address.toString(16) + ': js!JSFunction: ' + Content);
    }

    Display() {
        this.Logger(this);
        this.Logger('Flags: ' + this.Flags);
    }
}

class __JSSymbol {
    constructor(Addr) {
        this._Obj = host.createPointerObject(
            Addr,
            Module,
            'js::Symbol*'
        );
    }

    toString() {
        const Desc = new __JSString(this._Obj.description_.address);
        return 'Symbol(' + Desc + ')';
    }

    Logger(Content) {
        logln(this.Obj_.address.toString(16) + ': js!js::Symbol: ' + Content);
    }

    Display() {
        this.Logger(this);
    }
}

class __JSArrayBuffer {
    constructor(Addr) {
        this._Obj = host.createPointerObject(
            Addr,
            Module,
            'js::ArrayBufferObject*'
        );

        const ArrayBufferObjectSize = host.getModuleType(Module, 'js::ArrayBufferObject').size;
        // static const uint8_t DATA_SLOT = 0;
        // static const uint8_t BYTE_LENGTH_SLOT = 1;
        const ByteLengthSlotAddr = Addr.add(ArrayBufferObjectSize).add(1 * 8);
        const ByteLengthSlot = read_u64(ByteLengthSlotAddr);
        this._ByteLength = new __JSInt32(ByteLengthSlot)._Value;
        // static const uint8_t FIRST_VIEW_SLOT = 2;
        // static const uint8_t FLAGS_SLOT = 3;
        const FlagsAddr = Addr.add(ArrayBufferObjectSize).add(3 * 8);
        const FlagsSlot = read_u64(FlagsAddr);
        this._Flags = new __JSInt32(FlagsSlot)._Value;
    }

    get Flags() {
        //  enum BufferKind {
        //      PLAIN               = 0, // malloced or inline data
        //      WASM                = 1,
        //      MAPPED              = 2,
        //      EXTERNAL            = 3,
        //      KIND_MASK           = 0x3
        //  };
        // enum ArrayBufferFlags {
        //     // The flags also store the BufferKind
        //     BUFFER_KIND_MASK    = BufferKind::KIND_MASK,
        //     DETACHED            = 0x4,
        //     // The dataPointer() is owned by this buffer and should be released
        //     // when no longer in use. Releasing the pointer may be done by freeing,
        //     // invoking a dereference callback function, or unmapping, as
        //     // determined by the buffer's other flags.
        //     //
        //     // Array buffers which do not own their data include buffers that
        //     // allocate their data inline, and buffers that are created lazily for
        //     // typed objects with inline storage, in which case the buffer points
        //     // directly to the typed object's storage.
        //     OWNS_DATA           = 0x8,
        //     // This array buffer was created lazily for a typed object with inline
        //     // data. This implies both that the typed object owns the buffer's data
        //     // and that the list of views sharing this buffer's data might be
        //     // incomplete. Any missing views will be typed objects.
        //     FOR_INLINE_TYPED_OBJECT = 0x10,
        //     // Views of this buffer might include typed objects.
        //     TYPED_OBJECT_VIEWS  = 0x20,
        //     // This PLAIN or WASM buffer has been prepared for asm.js and cannot
        //     // henceforth be transferred/detached.
        //     FOR_ASMJS           = 0x40
        // };
        const BufferKinds = {
            0 : 'PLAIN',
            1 : 'WASM',
            2 : 'MAPPED',
            3 : 'EXTERNAL'
        };

        const BufferKind = BufferKinds[this._Flags.bitwiseAnd(3).asNumber()];
        const ArrayBufferFlags = [
            'BufferKind(' + BufferKind + ')'
        ];

        const ArrayBufferFlagsConstants = {
            [0x04] : 'DETACHED',
            [0x08] : 'OWNS_DATA',
            [0x10] : 'FOR_INLINE_TYPED_OBJECT',
            [0x20] : 'TYPED_OBJECT_VIEWS',
            [0x40] : 'FOR_ASMJS'
        };

        for(const Key in ArrayBufferFlagsConstants) {
            if(this._Flags.bitwiseAnd(host.parseInt64(Key)).compareTo(0) != 0) {
                ArrayBufferFlags.push(ArrayBufferFlagsConstants[Key]);
            }
        }

        return ArrayBufferFlags.join(' | ');
    }

    get ByteLength() {
        return this._ByteLength;
    }

    toString() {
        return 'ArrayBuffer({ByteLength:' + this._ByteLength + ', ...})';
    }

    Logger(Content) {
        logln(this._Obj.address.toString(16) + ': js!js::ArrayBufferObject: ' + Content);
    }

    Display() {
        this.Logger('ByteLength: ' + this.ByteLength);
        this.Logger('     Flags: ' + this.Flags);
        this.Logger('   Content: ' + this);
    }
}

class __JSTypedArray {
    constructor(Addr) {
        this._Obj = host.createPointerObject(
            Addr,
            Module,
            'js::TypedArrayObject*'
        );

        const Group = this._Obj.group_.value;
        this._TypeName = host.memory.readString(Group.clasp_.name)
        const Sizes = {
            'Float64Array' : 8,
            'Float32Array' : 4,
            'Uint32Array' : 4,
            'Int32Aray' : 4,
            'Uint16Array' : 2,
            'Int16Array' : 2,
            'Uint8Array' : 1,
            'Uint8ClampedArray' : 1,
            'Int8Array' : 1
        };
        this._ElementSize = Sizes[this._TypeName];

        const TypedArrayObjectSize = host.getModuleType(Module, 'js::TypedArrayObject').size;
        // static const size_t BUFFER_SLOT = 0;
        // static const size_t LENGTH_SLOT = 1;
        const LengthSlotAddr = Addr.add(TypedArrayObjectSize).add(1 * 8);
        const LengthSlot = read_u64(LengthSlotAddr);
        this._Length = new __JSInt32(LengthSlot)._Value;
        this._ByteLength = this._Length * this._ElementSize;
        // static const size_t BYTEOFFSET_SLOT = 2;
        const ByteOffsetSlotAddr = Addr.add(TypedArrayObjectSize).add(2 * 8);
        const ByteOffsetSlot = read_u64(ByteOffsetSlotAddr);
        this._ByteOffset = new __JSInt32(ByteOffsetSlot)._Value;
        // static const size_t RESERVED_SLOTS = 3;
    }

    get Type() {
        return this._TypeName;
    }

    get ByteOffset() {
        return this._ByteOffset;
    }

    get ByteLength() {
        return this._ByteLength;
    }

    get Length() {
        return this._Length;
    }

    toString() {
        return this._TypeName + '({Length:' + this._Length + ', ...})';
    }

    Logger(Content) {
        logln(this._Obj.address.toString(16) + ': js!js::TypedArrayObject: ' + Content);
    }

    Display() {
        this.Logger('      Type: ' + this.Type);
        this.Logger('    Length: ' + this.Length);
        this.Logger('ByteLength: ' + this.ByteLength);
        this.Logger('ByteOffset: ' + this.ByteOffset);
        this.Logger('   Content: ' + this);
    }
}

class __JSMap {
    constructor(Addr) {
        this._Addr = Addr;
    }

    // XXX: TODO
    toString() {
        return 'new Map(...)';
    }

    Logger(Content) {
        logln(this._Addr.toString(16) + ': js!js::MapObject: ' + Content);
    }

    Display() {
        this.Logger('Content: ' + this);
    }
}

class __JSDouble {
    constructor(Addr) {
        this._Addr = Addr;
    }

    toString() {
        const U32 = new Uint32Array([
            this._Addr.getLowPart(),
            this._Addr.getHighPart()
        ]);
        const F64 = new Float64Array(U32.buffer);
        return F64[0];
    }

    Logger(Content) {
        logln(this._Addr.toString(16) + ': JSVAL_TYPE_DOUBLE: ' + Content);
    }

    Display() {
        this.Logger(this);
    }
}

const Names2Types = {
    'Function' : __JSFunction,
    'Array' : __JSArray,
    'ArrayBuffer' : __JSArrayBuffer,
    'Map' : __JSMap,
    'Int32' : __JSInt32,
    'String' : __JSString,
    'Boolean' : __JSBoolean,
    'Null' : __JSNull,
    'Undefined' : __JSUndefined,
    'Symbol' : __JSSymbol,
    'Double' : __JSDouble,
    'Magic' : __JSMagic,
    'Arguments' : __JSArgument,

    'Float64Array' : __JSTypedArray,
    'Float32Array' : __JSTypedArray,
    'Uint32Array' : __JSTypedArray,
    'Int32Array' : __JSTypedArray,
    'Uint16Array' : __JSTypedArray,
    'Int16Array' : __JSTypedArray,
    'Uint8Array' : __JSTypedArray,
    'Uint8ClampedArray' : __JSTypedArray,
    'Int8Array' : __JSTypedArray
};

class __JSObject {
    /* JSObject.h
     * A JavaScript object.
     *
     * This is the base class for all objects exposed to JS script (as well as some
     * objects that are only accessed indirectly). Subclasses add additional fields
     * and execution semantics. The runtime class of an arbitrary JSObject is
     * identified by JSObject::getClass().
     *
     * The members common to all objects are as follows:
     *
     * - The |group_| member stores the group of the object, which contains its
     *   prototype object, its class and the possible types of its properties.
     *
     * - The |shapeOrExpando_| member points to (an optional) guard object that JIT
     *   may use to optimize. The pointed-to object dictates the constraints
     *   imposed on the JSObject:
     *      nullptr
     *          - Safe value if this field is not needed.
     *      js::Shape
     *          - All objects that might point |shapeOrExpando_| to a js::Shape
     *            must follow the rules specified on js::ShapedObject.
     *      JSObject
     *          - Implies nothing about the current object or target object. Either
     *            of which may mutate in place. Store a JSObject* only to save
     *            space, not to guard on.
     */
    constructor(Addr) {
        this._Addr = Addr;
        this._Obj = host.createPointerObject(
            this._Addr,
            Module,
            'JSObject*'
        );

        this._Properties = [];
        const Group = this._Obj.group_.value;
        this._ClassName = host.memory.readString(Group.clasp_.name);
        const NonNative = Group.clasp_.flags.bitwiseAnd(CLASS_NON_NATIVE).compareTo(0) != 0;
        if(NonNative) {
            return;
        }

        const Shape = host.createPointerObject(
            this._Obj.shapeOrExpando_.address,
            Module,
            'js::Shape*'
        );

        const NativeObject = host.createPointerObject(Addr, Module, 'js::NativeObject*');

        if(this._ClassName == 'Array') {

            //
            // Optimization for 'length' property if 'Array' cf
            // js::ArrayObject::length / js::GetLengthProperty
            //

            const ObjectElements = heapslot_to_objectelements(NativeObject.elements_.address);
            this._Properties.push('length : ' + ObjectElements.length);
            return;
        }

        //
        // Walk the list of Shapes and get the property names
        //

        const Properties = {};
        let CurrentShape = Shape;
        while(CurrentShape.parent.value.address.compareTo(0) != 0) {
            const SlotIdx = CurrentShape.immutableFlags.bitwiseAnd(SLOT_MASK).asNumber();
            Properties[SlotIdx] = get_property_from_shape(CurrentShape);
            CurrentShape = CurrentShape.parent.value;
        }

        //
        // Walk the slots to get the values now (check NativeGetPropertyInline/GetExistingProperty)
        //

        const NativeObjectTypeSize = host.getModuleType(Module, 'js::NativeObject').size;
        const NativeObjectElements = NativeObject.address.add(NativeObjectTypeSize);
        const NativeObjectSlots = NativeObject.slots_.address;
        const Max = Shape.immutableFlags.bitwiseShiftRight(FIXED_SLOTS_SHIFT).asNumber();
        for(let Idx = 0; Idx < Object.keys(Properties).length; Idx++) {

            //
            // Check out NativeObject::getSlot()
            //

            const PropertyName = Properties[Idx];
            let PropertyValue = undefined;
            let ElementAddr = undefined;
            if(Idx < Max) {
                ElementAddr = NativeObjectElements.add(Idx * 8);
            } else {
                ElementAddr = NativeObjectSlots.add((Idx - Max) * 8);
            }

            const JSValue = read_u64(ElementAddr);
            PropertyValue = jsvalue_to_instance(JSValue);
            this._Properties.push(PropertyName + ' : ' + PropertyValue);
        }
    }

    get Properties() {
        return this._Properties;
    }

    get ClassName() {
        return this._ClassName;
    }

    toString() {
        if(this._ClassName != 'Object' && Names2Types.hasOwnProperty(this._ClassName)) {
            const Type = Names2Types[this._ClassName];
            return new Type(this._Addr).toString();
        }

        if(this._ClassName != 'Object') {
            return this._ClassName;
        }

        if(this._Properties != undefined && this._Properties.length > 0) {
            return '{' + this._Properties.join(', ') + '}';
        }

        if(this._ClassName == 'Object') {
            return '[Object]';
        }

        return 'Dunno';
    }

    Logger(Content) {
        logln(this._Addr.toString(16) + ': js!JSObject: ' + Content);
    }

    Display() {
        this.Logger('Content: ' + this);

        //
        // If the class name is not Object then it means the toString() method
        // might already have displayed the properties.
        // {foo:'bar'} VS Math.
        //

        if(this._ClassName != 'Object') {
            this.Logger('Properties: {' + this._Properties.join(', ') + '}');
        }
    }
}

Names2Types['Object'] = __JSObject;

function smdump_jsobject(Addr, Type = null) {
    init();

    if(Addr.hasOwnProperty('address')) {
        Addr = Addr.address;
    }

    let ClassName;
    if(Type == 'Object' || Type == null) {
        const JSObject = new __JSObject(Addr);
        ClassName = JSObject.ClassName;
        if(!Names2Types.hasOwnProperty(ClassName)) {
            JSObject.Display();
        }
    } else {
        ClassName = Type;
    }

    if(Names2Types.hasOwnProperty(ClassName)) {
        const Inst = new Names2Types[ClassName](Addr);
        Inst.Display();
    }
}

function smdump_jsvalue(Addr) {
    init();

    if(Addr == undefined) {
        logln('!smdump_jsvalue <jsvalue object addr>');
        return;
    }

    //
    // Ensure Addr is an unsigned value. If we don't do this
    // the shift operations don't behave the way we want them to.
    //

    Addr = Addr.bitwiseAnd(host.parseInt64('0xffffffffffffffff'));
    const JSValue = new __JSValue(Addr);
    if(!Tag2Names.hasOwnProperty(JSValue.Tag)) {
        logln('Tag ' +  JSValue.Tag.toString(16) + ' not recognized');
        return;
    }

    const Name = Tag2Names[JSValue.Tag];
    return smdump_jsobject(JSValue.Payload, Name);
}

function init() {
    if(Module != null) {
        return;
    }

    const Xul = host.currentProcess.Modules.Any(
        p => p.Name.toLowerCase().endsWith('xul.dll')
    );

    if(Xul) {
        Module = 'xul.dll';
        logln('Detected xul.dll, using it as js module.');
        return;
    }

    Module = 'js.exe';
}

function ion_insertbp() {
    // XXX: Having the current frame would be better.. but not sure if
    // this is something possible?
    const CurrentThread = host.currentThread;
    const LowestFrame = CurrentThread.Stack.Frames[0];
    const LocalVariables = LowestFrame.LocalVariables;
    const CodeGenerator = LocalVariables.this;
    if(CodeGenerator === undefined ||
       CodeGenerator.targetType.toString() != 'js::jit::CodeGenerator *') {
        logln('The script expects `this` to be a js::jit::CodeGenerator in the lowest frame.');
        return;
    }

    const JITBuffer = CodeGenerator.masm.masm.m_formatter.m_buffer.m_buffer;
    // XXX: So sounds like I can't do JITBuffer.mBegin[JITBuffer.mLength] = 0xcc,
    // so here I am writing ugly things :x
    const BreakpointAddress = JITBuffer.mBegin.address.add(JITBuffer.mLength);
    logln(`JIT buffer is at ${hex(JITBuffer.mBegin.address)}`);
    logln(`Writing breakpoint at ${hex(BreakpointAddress)}`);
    host.evaluateExpression(`*(char*)${hex(BreakpointAddress)} = 0xcc`);
    JITBuffer.mLength += 1;
}

let Context = undefined;
function dump_nursery_stats(Nursery) {
    logln(`${hex(Nursery.address)}: js::Nursery`);
    const ChunkCountLimit = Nursery.chunkCountLimit_;
    const NurseryChunk = host.createPointerObject(
        0,
        Module,
        'js::NurseryChunk*'
    );
    const ChunkSize = NurseryChunk.dereference().targetType.size;
    const MaxSize = ChunkCountLimit.multiply(ChunkSize);
    logln(` ChunkCountLimit: ${hex(ChunkCountLimit)} (${MaxSize / 1024 / 1024} MB)`);
    const Capacity = Nursery.capacity_;
    logln(`        Capacity: ${hex(Capacity)} bytes`)
    const CurrentChunk = Nursery.currentStartPosition_;
    logln(`    CurrentChunk: ${hex(CurrentChunk)}`);
    const Position = Nursery.position_;
    logln(`        Position: ${hex(Position)}`);
    logln('          Chunks:');
    const Chunks = Nursery.chunks_;
    for(let Idx = 0; Idx < Chunks.mLength; Idx++) {
        const Chunk = Chunks.mBegin[Idx];
        const StartAddress = Chunk.data.address;
        const EndAddress = StartAddress.add(ChunkSize).subtract(1);
        const PaddedIdx = Idx.toString().padStart(2, '0');
        logln(`            ${PaddedIdx}: [${hex(StartAddress)} - ${hex(EndAddress)}]`);
    }
}

function in_nursery(Addr) {
    init();

    if(Addr == undefined) {
        logln('!in_nursery <object addr>');
        return;
    }

    //
    // Find 'cx' the JSContext somewhere..
    //

    if(Context == undefined) {
        const CurrentThread = host.currentThread;
        for(const Frame of CurrentThread.Stack.Frames) {
            const Parameters = Frame.Parameters;
            if(Parameters == undefined) {
                continue;
            }

            Context = Parameters.cx;
            if(Context == undefined ||
            Context.targetType.toString() != 'JSContext *') {
                continue;
            }

            logln(`Caching JSContext @${hex(Context.address)} for next times.`);
            break;
        }
    } else {
        logln(`Using previously cached JSContext @${hex(Context.address)}`);
    }

    if(Context == undefined) {
        logln('Could not locate a JSContext in this call-stack.');
        return;
    }

    //
    // Get the Nursery.
    //

    const Nursery = Context.runtime_.value.gc.nursery_.value;
    const NurseryChunk = host.createPointerObject(
        0,
        Module,
        'js::NurseryChunk*'
    );
    const ChunkUseableSize = NurseryChunk.data.targetType.size;

    //
    // Dump some stats about the Nursery.
    //

    dump_nursery_stats(Nursery);
    logln('-------');

    //
    // Iterate through the chunk regions.
    //

    const Chunks = Nursery.chunks_;
    let FoundChunk = undefined;
    for(let Idx = 0; Idx < Chunks.mLength; Idx++) {
        const Chunk = Chunks.mBegin[Idx];
        const StartAddress = Chunk.data.address;
        const EndAddress = StartAddress.add(ChunkUseableSize);
        if(Addr.compareTo(StartAddress) >= 0 &&
           Addr.compareTo(EndAddress) < 0) {
               FoundChunk = Chunk;
               break;
           }
    }

    if(FoundChunk != undefined) {
        logln(`${hex(Addr)} has been found in the js::NurseryChunk @${hex(FoundChunk.data.address)}!`);
        return;
    }

    logln(`${hex(Addr)} hasn't been found be in any Nursery js::NurseryChunk.`);
}

function initializeScript() {
    return [
        new host.apiVersionSupport(1, 3),
        new host.functionAlias(
            smdump_jsvalue,
            'smdump_jsvalue'
        ),
        new host.functionAlias(
            smdump_jsobject,
            'smdump_jsobject'
        ),
        new host.functionAlias(
            ion_insertbp,
            'ion_insertbp'
        ),
        new host.functionAlias(
            in_nursery,
            'in_nursery'
        )
    ];
}


================================================
FILE: telescope/README.md
================================================
# telescope.js

`telescope.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debugger-scripting) debugger extension for WinDbg that mirrors the `dereference`/`telescope` command from [GEF](https://github.com/hugsy/gef). It works on crash-dumps, live debugging, and TTD traces. Both for user and kernel-mode.

Idea from [@\_\_awe](https://twitter.com/__awe).

## Usage

Run `.scriptload telescope.js` to load the script. You can invoke the telescope feature with `!telescope <addr>` or programatically via `dx @$createchain(<addr>)`.

## Examples

* From an x64 TTD execution trace:

```text
0:000> !telescope @rsp
0x0000005be1ffcec0|+0x0000: 0xe1000205e1ffdd48 (Unknown)
0x0000005be1ffcec8|+0x0008: 0x00007ff700000006 (Unknown)
0x0000005be1ffced0|+0x0010: 0x000001fce5928840 (VirtualAlloced) -> 0x0000005be1ffd0b8 (Stack) -> 0x000001fce5928840 (VirtualAlloced) [...]
0x0000005be1ffced8|+0x0018: 0x0000005be1ffdb68 (Stack) -> 0x000001fce5928840 (VirtualAlloced) -> 0x0000005be1ffd0b8 (Stack) -> 0x000001fce5928840 (VirtualAlloced) [...]
0x0000005be1ffcee0|+0x0020: 0x000001fce634afa0 (VirtualAlloced) -> 0x0000000800000b50 (Unknown)
0x0000005be1ffcee8|+0x0028: 0x00004a54b4bb11e0 (Unknown)
0x0000005be1ffcef0|+0x0030: 0x0000000000000008 (Unknown)
0x0000005be1ffcef8|+0x0038: 0x0000000000000000 (Unknown)
0x0000005be1ffcf00|+0x0040: 0x0000005be1ffdbc8 (Stack) -> 0x000001fce6cb3eb8 (VirtualAlloced) -> 0x00007ff77704e920 (js.exe (.rdata)) -> 0x00007ff776755aa0 (js.exe (.text)) -> mov     rax,qword ptr [rcx-18h] ; test    byte ptr [rax+23h],2
0x0000005be1ffcf08|+0x0048: 0x00007ff7766b4546 (js.exe (.text)) -> test    rax,rax ; je      js!mozilla::Vector<char *,0,js::TempAllocPolicy>::growStorageBy+0x395 (00007ff7`766b4805)
@$telescope(@rsp)
```

* Accessing the chain programatically via `createchain`:

```text
0:000> dx @$createchain(0x0000005be1ffcf08)
@$createchain(0x0000005be1ffcf08)                 : 0x00007ff7766b4546 (js.exe (.text)) -> test    rax,rax ; je      js!mozilla::Vector<char *,0,js::TempAllocPolicy>::growStorageBy+0x395 (00007ff7`766b4805)
    [0x0]            : 0x00007ff7766b4546 (js.exe (.text))
    [0x1]            : test    rax,rax ; je      js!mozilla::Vector<char *,0,js::TempAllocPolicy>::growStorageBy+0x395 (00007ff7`766b4805)

0:000> dx -r1 @$createchain(0x0000005be1ffcf08)[0]
@$createchain(0x0000005be1ffcf08)[0]                 : 0x00007ff7766b4546 (js.exe (.text))
    Addr             : 0x5be1ffcf08
    Value            : 0x7ff7766b4546
    AddrRegion       : Stack rw-
    ValueRegion      : Image C:\work\codes\blazefox\js-release\js.exe (.text) r-x
    Name             : js.exe (.text)
    Last             : false

0:000> dx -r1 @$createchain(0x0000005be1ffcf08)[1]
@$createchain(0x0000005be1ffcf08)[1]                 : test    rax,rax ; je      js!mozilla::Vector<char *,0,js::TempAllocPolicy>::growStorageBy+0x395 (00007ff7`766b4805)
    Addr             : 0x7ff7766b4546
    Value            : 0x2b6840fc08548
    AddrRegion       : Image C:\work\codes\blazefox\js-release\js.exe (.text) r-x
    Name             : Unknown
    Last             : true
```

* From an x86 live-session:

```text
0:001> !telescope @esp
0x00d7ff44|+0x0000: 0x77dcb3a9 (ntdll.dll (.text)) -> jmp     ntdll!DbgUiRemoteBreakin+0x42 (77dcb3b2) ; xor     eax,eax
0x00d7ff48|+0x0004: 0x1911c0a3 (Unknown)
0x00d7ff4c|+0x0008: 0x77dcb370 (ntdll.dll (.text)) -> push    8 ; push    offset ntdll!QueryRegistryValue+0x13d2 (77e29538)
0x00d7ff50|+0x000c: 0x77dcb370 (ntdll.dll (.text)) -> push    8 ; push    offset ntdll!QueryRegistryValue+0x13d2 (77e29538)
0x00d7ff54|+0x0010: 0x00000000 (Unknown)
0x00d7ff58|+0x0014: 0x00d7ff48 (Stack) -> 0x1911c0a3 (Unknown)
0x00d7ff5c|+0x0018: 0x00000000 (Unknown)
0x00d7ff60|+0x001c: 0x00d7ffcc (Stack) -> 0x00d7ffe4 (Stack) -> 0xffffffff (Unknown)
0x00d7ff64|+0x0020: 0x77d986d0 (ntdll.dll (.text)) -> mov     edi,edi ; push    ebp
0x00d7ff68|+0x0024: 0x6e24aaeb (Unknown)
@$telescope(@esp)
```

* From an x64 kernel live-session

```
kd> !telescope 0xfffff8000d2dca78
0xfffff8000d2dca78|+0x0000: 0x0000000000000000 (Unknown)
0xfffff8000d2dca80|+0x0008: 0x0000000000000000 (Unknown)
0xfffff8000d2dca88|+0x0010: 0x0000000000000000 (Unknown)
0xfffff8000d2dca90|+0x0018: 0xfffff8000d03e030 (Image ntkrnlmp.exe (.text)) -> sub     rsp,28h ; and     qword ptr [rsp+28h],0
0xfffff8000d2dca98|+0x0020: 0x0000000000000000 (Unknown)
0xfffff8000d2dcaa0|+0x0028: 0x0000000000000000 (Unknown)
0xfffff8000d2dcaa8|+0x0030: 0xfffff8000d2d9e48 (Image ntkrnlmp.exe (CACHEALI)) -> 0xfffff8000d2dcaa8 (Image ntkrnlmp.exe (CACHEALI)) [...]
0xfffff8000d2dcab0|+0x0038: 0xfffff8000d2d9e48 (Image ntkrnlmp.exe (CACHEALI)) -> 0xfffff8000d2dcaa8 (Image ntkrnlmp.exe (CACHEALI)) -> 0xfffff8000d2d9e48 (Image ntkrnlmp.exe (CACHEALI)) [...]
0xfffff8000d2dcab8|+0x0040: 0x0000000000000000 (Unknown)
0xfffff8000d2dcac0|+0x0048: 0x0000000000000000 (Unknown)
@$telescope(0xfffff8000d2dca78)
```


================================================
FILE: telescope/telescope.js
================================================
// Axel '0vercl0k' Souchet - 7th December 2018

'use strict';

const log = host.diagnostics.debugLog;
const logln = p => host.diagnostics.debugLog(p + '\n');

//
// Config variables.
//

// This is the number of lines the !telescope command displays.
const DefaultNumberOfLines = 10;

// This is the number of instructions to disassemble when a code pointer is encountered.
const DefaultNumberOfInstructions = 3;

// This is the maximum number of characters displayed for strings in the !telescope output.
const DefaultMaxStringLength = 15;

//
// Utility functions.
//

function ReadU64(Addr) {
    let Value = null;
    try {
        Value = host.memory.readMemoryValues(
            Addr, 1, 8
        )[0];
    } catch (e) {
    }

    return Value;
}

function ReadU32(Addr) {
    let Value = null;
    try {
        Value = host.memory.readMemoryValues(
            Addr, 1, 4
        )[0];
    } catch (e) {
    }

    return Value;
}

function ReadU16(Addr) {
    let Value = null;
    try {
        Value = host.memory.readMemoryValues(
            Addr, 1, 2
        )[0];
    } catch (e) {
    }

    return Value;
}

function ReadString(Addr, MaxLength) {
    let Value = null;
    try {
        Value = host.memory.readString(Addr);
    } catch (e) {
        return null;
    }

    if (Value.length > MaxLength) {
        return Value.substr(0, MaxLength);
    }

    return Value;
}

function ReadWideString(Addr) {
    let Value = null;
    try {
        Value = host.memory.readWideString(Addr);
    } catch (e) {
    }

    return Value;
}

function Disassemble(Addr) {
    const Code = host.namespace.Debugger.Utility.Code;
    const Disassembler = Code.CreateDisassembler(
        PointerSize == 8 ? 'X64' : 'X86'
    );
    const Instrs = Array.from(Disassembler.DisassembleInstructions(Addr).Take(
        DefaultNumberOfInstructions
    ));

    return Instrs.map(

        //
        // Clean up the assembly.
        // Turn the below:
        //   'mov     rbx,qword ptr [00007FF8D3525660h] ; test    rbx,rbx ; je     00007FF8D34FC2EB'
        // Into:
        //   'mov rbx,qword ptr [00007FF8D3525660h] ; test rbx,rbx ; je 00007FF8D34FC2EB'
        //

        p => p.toString().replace(/[ ]+/g, ' ')
    ).join(' ; ');
}

function FormatU64(Addr) {
    return '0x' + Addr.toString(16).padStart(16, '0');
}

function FormatU32(Addr) {
    return '0x' + Addr.toString(16).padStart(8, '0');
}

function FormatString(Str) {
    if (Str.length > DefaultMaxStringLength) {
        return Str.substr(0, DefaultMaxStringLength) + '...'
    }

    return Str;
}

function BitSet(Value, Bit) {
    return Value.bitwiseAnd(Bit).compareTo(0) != 0;
}

//
// Initialization / global stuff.
//

let Initialized = false;
let ReadPtr = null;
let PointerSize = null;
let FormatPtr = null;
let IsTTD = false;
let IsUser = false;
let IsKernel = false;
let VaSpace = [];

function* SectionHeaders(BaseAddress) {
    if (IsKernel && ReadU32(BaseAddress) == null) {

        //
        // If we can't read the module, then..bail :(.
        // XXX: Fix this? Session space? Paged out?
        //

        logln('Cannot read ' + BaseAddress.toString(16) + ', skipping.');
        return;
    }

    // 0:000> dt _IMAGE_DOS_HEADER e_lfanew
    //   +0x03c e_lfanew : Int4B
    const NtHeaders = BaseAddress.add(ReadU32(BaseAddress.add(0x3c)));
    // 0:000> dt _IMAGE_NT_HEADERS64 FileHeader
    //    +0x004 FileHeader : _IMAGE_FILE_HEADER
    // 0:000> dt _IMAGE_FILE_HEADER NumberOfSections SizeOfOptionalHeader
    //    +0x002 NumberOfSections : Uint2B
    //    +0x010 SizeOfOptionalHeader : Uint2B
    const NumberOfSections = ReadU16(NtHeaders.add(0x4 + 0x2));
    const SizeOfOptionalHeader = ReadU16(NtHeaders.add(0x4 + 0x10));
    // 0:000> dt _IMAGE_NT_HEADERS64 OptionalHeader
    //   +0x018 OptionalHeader : _IMAGE_OPTIONAL_HEADER64
    const OptionalHeader = NtHeaders.add(0x18);
    const SectionHeaders = OptionalHeader.add(SizeOfOptionalHeader);
    // 0:000> ?? sizeof(_IMAGE_SECTION_HEADER)
    // unsigned int64 0x28
    const SizeofSectionHeader = 0x28;
    for (let Idx = 0; Idx < NumberOfSections; Idx++) {
        const SectionHeader = SectionHeaders.add(
            Idx.multiply(SizeofSectionHeader)
        );
        // 0:000> dt _IMAGE_SECTION_HEADER Name
        //    +0x000 Name             : [8] UChar
        const Name = ReadString(SectionHeader, 8);
        // 0:000> dt _IMAGE_SECTION_HEADER VirtualAddress
        //    +0x00c VirtualAddress : Uint4B
        const Address = BaseAddress.add(
            ReadU32(SectionHeader.add(0xc))
        );
        // 0:000> dt _IMAGE_SECTION_HEADER SizeOfRawData
        //    +0x08 Misc : Uint4B
        // XXX: Take care of alignment?
        const VirtualSize = ReadU32(SectionHeader.add(0x08));
        // 0:000> dt _IMAGE_SECTION_HEADER Characteristics
        //    +0x024 Characteristics : Uint4B
        const Characteristics = ReadU32(SectionHeader.add(0x24));
        const Properties = [
            '-',
            '-',
            '-'
        ];

        // The section can be read.
        const IMAGE_SCN_MEM_READ = host.Int64(0x40000000);
        if (BitSet(Characteristics, IMAGE_SCN_MEM_READ)) {
            Properties[0] = 'r';
        }

        if (IsKernel) {
            const IMAGE_SCN_MEM_DISCARDABLE = host.Int64(0x2000000);
            if (BitSet(Characteristics, IMAGE_SCN_MEM_DISCARDABLE)) {
                Properties[0] = '-';
            }
        }

        // The section can be written to.
        const IMAGE_SCN_MEM_WRITE = host.Int64(0x80000000);
        if (Characteristics.bitwiseAnd(IMAGE_SCN_MEM_WRITE).compareTo(0) != 0) {
            Properties[1] = 'w';
        }

        // The section can be executed as code.
        const IMAGE_SCN_MEM_EXECUTE = host.Int64(0x20000000);
        if (Characteristics.bitwiseAnd(IMAGE_SCN_MEM_EXECUTE).compareTo(0) != 0) {
            Properties[2] = 'x';
        }

        yield new _Region(
            Address,
            VirtualSize,
            Name,
            Properties.join('')
        );
    }
}

function HandleTTD() {
    const CurrentSession = host.currentSession;

    //
    // Grab addressable chunks.
    //

    logln('Populating the VA space with TTD.Data.Heap..');
    const CurrentThread = host.currentThread;
    const Position = CurrentThread.TTD.Position;
    const Chunks = CurrentSession.TTD.Data.Heap().Where(
        p => p.TimeStart.compareTo(Position) < 0 &&
            p.Action == 'Alloc'
    );

    for (const Chunk of Chunks) {
        VaSpace.push(new _Region(
            Chunk.Address,
            Chunk.Size,
            'Heap',
            'rw-'
        ));
    }

    //
    // Grab virtual allocated memory regions.
    //

    logln('Populating the VA space with VirtualAllocated regions..');
    const VirtualAllocs = CurrentSession.TTD.Calls(
        'kernelbase!VirtualAlloc'
    ).Where(
        p => p.TimeStart.compareTo(Position) < 0
    );

    for (const VirtualAlloc of VirtualAllocs) {
        VaSpace.push(new _Region(
            VirtualAlloc.ReturnValue,
            VirtualAlloc.Parameters[1],
            'VirtualAlloced',
            // XXX: parse access
            'rw-'
        ));
    }

    //
    // Grab mapped view regions.
    //

    logln('Populating the VA space with MappedViewOfFile regions..');
    const MapViewOfFiles = CurrentSession.TTD.Calls(
        'kernelbase!MapViewOfFile'
    ).Where(
        p => p.TimeStart.compareTo(Position) < 0
    );

    for (const MapViewOfFile of MapViewOfFiles) {
        VaSpace.push(new _Region(
            MapViewOfFile.ReturnValue,
            host.Int64(0x1000),
            'MappedView',
            // XXX: parse access
            'rw-'
        ));
    }
}

function HandleUser() {

    //
    // Enumerate the modules.
    //

    logln('Populating the VA space with modules..');
    const CurrentProcess = host.currentProcess;
    for (const Module of CurrentProcess.Modules) {

        //
        // Iterate over the section headers of the module.
        //

        for (const Section of SectionHeaders(Module.BaseAddress)) {
            VaSpace.push(new _Region(
                Section.BaseAddress,
                Section.Size,
                'Image ' + Module.Name + ' (' + Section.Name + ')',
                Section.Properties
            ));
        }

        //
        // Add a catch all in case a pointer points inside the PE but not
        // inside any sections (example of this is the PE header).
        //

        VaSpace.push(new _Region(
            Module.BaseAddress,
            Module.Size,
            'Image ' + Module.Name,
            'r--'
        ));
    }

    //
    // Enumerates the TEBs and the stacks.
    //

    logln('Populating the VA space with TEBs & thread stacks..');
    for (const Thread of CurrentProcess.Threads) {
        const Teb = Thread.Environment.EnvironmentBlock;

        //
        // TEB!
        //
        // In the case where you have broken `ntdll` symbols, you might not have
        // the definition of the `_TEB` structure. In this case, the structured
        // `Teb` object above is undefined (like in issues #2). So we try to be resilient
        // against that in the below.
        //

        if (Teb == undefined) {
            const General = host.namespace.Debugger.State.PseudoRegisters.General;
            VaSpace.push(new _Region(
                General.teb.address,
                host.Int64(0x100),
                'Teb of ' + Thread.Id.toString(16),
                'rw-'
            ));

            continue;
        }

        VaSpace.push(new _Region(
            Teb.address,
            Teb.targetType.size,
            'Teb of ' + Thread.Id.toString(16),
            'rw-'
        ));

        //
        // Stacks!
        //

        const StackBase = Teb.NtTib.StackBase.address;
        const StackLimit = Teb.NtTib.StackLimit.address;
        VaSpace.push(new _Region(
            StackLimit,
            StackBase.subtract(StackLimit),
            'Stack',
            'rw-'
        ));
    }

    //
    // Get the PEB. Keep in mind we can run into the same symbol problem with the
    // PEB - so account for that.
    //

    logln('Populating the VA space with the PEB..');
    const Peb = CurrentProcess.Environment.EnvironmentBlock;

    if (Peb == undefined) {
        const General = host.namespace.Debugger.State.PseudoRegisters.General;
        VaSpace.push(new _Region(
            General.peb.address,
            host.Int64(0x1000),
            'Peb',
            'rw-'
        ));

        logln(`/!\\ Several regions have been skipped because nt!_TEB / nt!_PEB aren't available in your symbols.`);
    } else {
        VaSpace.push(new _Region(
            Peb.address,
            Peb.targetType.size,
            'Peb',
            'rw-'
        ));
    }
}

function HandleKernel() {

    //
    // Enumerate the kernel modules.
    //

    logln('Populating the VA space with kernel modules..');
    const CurrentSession = host.currentSession;
    const SystemProcess = CurrentSession.Processes.First(
        p => p.Name == 'System'
    );

    const MmUserProbeAddress = ReadPtr(
        host.getModuleSymbolAddress('nt', 'MmUserProbeAddress')
    );

    const KernelModules = SystemProcess.Modules.Where(
        p => p.BaseAddress.compareTo(MmUserProbeAddress) > 0
    );

    for (const Module of KernelModules) {

        //
        // Iterate over the section headers of the module.
        //

        for (const Section of SectionHeaders(Module.BaseAddress)) {
            VaSpace.push(new _Region(
                Section.BaseAddress,
                Section.Size,
                'Driver ' + Module.Name + ' (' + Section.Name + ')',
                Section.Properties
            ));
        }

        //
        // Add a catch all in case a pointer points inside the PE but not
        // inside any sections (example of this is the PE header).
        //

        VaSpace.push(new _Region(
            Module.BaseAddress,
            Module.Size,
            'Driver ' + Module.Name,
            'r--'
        ));
    }
}

function InitializeVASpace() {
    if (IsUser) {
        HandleUser();
    }

    if (IsTTD) {

        //
        // If we have a TTD target, let's do some more work.
        //

        HandleTTD();
    }

    if (IsKernel) {
        HandleKernel();
    }
}

function InitializeWrapper(Funct) {
    return Arg => {
        if (!Initialized) {
            const CurrentSession = host.currentSession;

            //
            // Initialize the ReadPtr function according to the PointerSize.
            //

            PointerSize = CurrentSession.Attributes.Machine.PointerSize;
            ReadPtr = PointerSize.compareTo(8) == 0 ? ReadU64 : ReadU32;
            FormatPtr = PointerSize.compareTo(8) == 0 ? FormatU64 : FormatU32;
            const TargetAttributes = CurrentSession.Attributes.Target;
            IsTTD = TargetAttributes.IsTTDTarget;
            IsUser = TargetAttributes.IsUserTarget;
            IsKernel = TargetAttributes.IsKernelTarget;

            //
            // One time initialization!
            //

            Initialized = true;
        }

        //
        // Once initialization is done, call into the function.
        //

        return Funct(Arg);
    };
}


//
// The meat!
//

class _Region {
    constructor(BaseAddress, Size, Name, Properties) {
        this.Name = Name;
        this.BaseAddress = BaseAddress;
        this.EndAddress = this.BaseAddress.add(Size);
        this.Size = Size;
        this.Properties = Properties;
        this.Executable = false;
        this.Readable = false;
        this.Writeable = false;
        if (Properties.indexOf('r') != -1) {
            this.Readable = true;
        }

        if (Properties.indexOf('w') != -1) {
            this.Writeable = true;
        }

        if (Properties.indexOf('x') != -1) {
            this.Executable = true;
        }

    }

    In(Addr) {
        const InBounds = Addr.compareTo(this.BaseAddress) >= 0 &&
            Addr.compareTo(this.EndAddress) < 0;
        return InBounds;
    }

    toString() {
        const Prop = [
            this.Readable ? 'r' : '-',
            this.Writeable ? 'w' : '-',
            this.Executable ? 'x' : '-'
        ];

        return this.Name + ' ' + Prop.join('');
    }
}

function AddressToRegion(Addr) {

    //
    // Map the address space with VA regions.
    //

    const Hits = VaSpace.filter(
        p => p.In(Addr)
    );

    //
    // Now, let's get the most precise region information by ordering
    // the hits by size.
    //

    const OrderedHits = Hits.sort(
        (a, b) => a.Size.compareTo(b.Size)
    );

    //
    // Return the most precise information we have!
    //

    return OrderedHits[0];
}

class _ChainEntry {
    constructor(Addr, Value) {
        this.Addr = Addr;
        this.Value = Value;
        this.AddrRegion = AddressToRegion(this.Addr);
        this.ValueRegion = AddressToRegion(this.Value);
        if (this.ValueRegion == undefined) {
            this.Name = 'Unknown';
        } else {

            //
            // Just keep the file name and strips off the path.
            //

            this.Name = this.ValueRegion.Name;
            this.Name = this.Name.substring(this.Name.lastIndexOf('\\') + 1);
        }
        this.Last = false;
    }

    Equals(Entry) {
        return this.Addr.compareTo(Entry.Addr) == 0;
    }

    toString() {
        const S = FormatPtr(this.Value) + ' (' + this.Name + ')';
        if (!this.Last) {
            return S;
        }

        //
        // We only provide disassembly if we know that the code is executeable.
        // And in order to know that, we need to have a valid `AddrRegion`.
        //

        if (this.AddrRegion != undefined && this.AddrRegion.Executable) {
            return Disassemble(this.Addr);
        }

        //
        // If we have a string stored in a heap allocation what happens is the following:
        //   - The extension does not know about heap, so `AddrRegion` for such a pointer
        //   would be `undefined`.
        //   - Even though it is undefined, we would like to display a string if there is any,
        //   instead of just the first qword.
        // So to enable the scenario to work, we allow to enter the below block with an `AddrRegion`
        // that is undefined.
        //

        if (this.AddrRegion == undefined || this.AddrRegion.Readable) {

            const IsPrintable = p => {
                return p != null &&
                    // XXX: ugly AF.
                    p.match(/^[a-z0-9!"#$%&'()*+,/\\.:;<=>?@\[\] ^_`{|}~-]+$/i) != null &&
                    p.length > 5
            };

            //
            // Maybe it points on a unicode / ascii string?
            //

            const Ansi = ReadString(this.Addr);

            if (IsPrintable(Ansi)) {
                return `${FormatPtr(this.Addr)} (Ascii(${FormatString(Ansi)}))`;
            }

            const Wide = ReadWideString(this.Addr);
            if (IsPrintable(Wide)) {
                return `${FormatPtr(this.Addr)} (Unicode(${FormatString(Wide)}))`;
            }
        }

        //
        // If we didn't find something better, fallback to the regular
        // output.
        //

        return S;
    }
}

class _Chain {
    constructor(Addr) {
        this.__Entries = [];
        this.__HasCycle = false;
        this.__Addr = Addr;
        while (this.FollowPtr()) { };
        this.__Length = this.__Entries.length;

        //
        // Tag the last entry as 'last'.
        //

        if (this.__Length >= 1) {
            this.__Entries[this.__Length - 1].Last = true;
        }
    }

    FollowPtr() {

        //
        // Attempt to follow the pointer.
        //

        const Value = ReadPtr(this.__Addr);
        if (Value == null) {

            //
            // We are done following pointers now!
            //

            return false;
        }

        //
        // Let's build an entry and evaluate what we want to do with it.
        //

        const Entry = new _ChainEntry(this.__Addr, Value);
        const DoesEntryExist = this.__Entries.find(
            p => p.Equals(Entry)
        );

        if (DoesEntryExist) {

            //
            // If we have seen this Entry before, it means there's a cycle
            // and we will stop there.
            //

            this.__HasCycle = true;
            return false;
        }

        //
        // This Entry is of interest, so let's add it in our list.
        //

        this.__Entries.push(Entry);
        this.__Addr = Value;
        return true;
    }

    toString() {
        if (this.__Entries.length == 0) {
            return '';
        }

        //
        // Iterate over the chain.
        //

        let S = this.__Entries.join(' -> ');

        //
        // Add a little something if we have a cycle so that the user knows.
        //

        if (this.__HasCycle) {
            S += ' [...]';
        }

        return S;
    }

    *[Symbol.iterator]() {
        for (const Entry of this.__Entries) {
            yield Entry;
        }
    }
}

function CreateChain(Addr) {

    //
    // Initialize the VA space.
    //

    InitializeVASpace();

    const Chain = new _Chain(Addr);
    VaSpace = [];
    return Chain;
}

function Telescope(Addr) {
    if (Addr == undefined) {
        logln('!telescope <addr>');
        return;
    }

    //
    // Initialize the VA space.
    //

    InitializeVASpace();

    const CurrentSession = host.currentSession;
    const Lines = DefaultNumberOfLines;
    const PointerSize = CurrentSession.Attributes.Machine.PointerSize;
    const FormatOffset = p => '0x' + p.toString(16).padStart(4, '0');

    for (let Idx = 0; Idx < Lines; Idx++) {
        const Offset = PointerSize.multiply(Idx);
        const CurAddr = Addr.add(Offset);
        const Chain = new _Chain(CurAddr);
        const Header = FormatPtr(CurAddr) + '|+' + FormatOffset(Offset);
        logln(Header + ': ' + Chain.toString());
    }

    VaSpace = [];
}

function initializeScript() {
    return [
        new host.apiVersionSupport(1, 3),
        new host.functionAlias(
            InitializeWrapper(Telescope),
            'telescope'
        ),
        new host.functionAlias(
            InitializeWrapper(CreateChain),
            'createchain'
        )
    ];
}
Download .txt
gitextract_yxreilaj/

├── LICENSE
├── Manifest/
│   ├── Manifest.1.xml
│   ├── ManifestVersion.txt
│   └── config.xml
├── README.md
├── basics/
│   ├── Int64.js
│   ├── breakpoint.js
│   ├── breakpoint2.js
│   ├── eval.js
│   ├── extendmodel.js
│   ├── extendmodel_1.js
│   ├── extendmodel_2.js
│   ├── extendmodel_2_1.js
│   └── readmemory.js
├── codecov/
│   ├── README.md
│   └── codecov.js
├── gdt/
│   ├── README.md
│   └── gdt.js
├── parse_eh_win64/
│   ├── README.md
│   └── parse_eh_win64.js
├── policybuffer/
│   ├── README.md
│   └── policybuffer.js
├── sm/
│   ├── README.md
│   └── sm.js
└── telescope/
    ├── README.md
    └── telescope.js
Download .txt
SYMBOL INDEX (267 symbols across 15 files)

FILE: basics/Int64.js
  function invokeScript (line 9) | function invokeScript() {

FILE: basics/breakpoint.js
  function handle_bp (line 9) | function handle_bp() {
  function invokeScript (line 19) | function invokeScript() {

FILE: basics/breakpoint2.js
  function handle_bp (line 9) | function handle_bp() {
  function invokeScript (line 25) | function invokeScript() {

FILE: basics/eval.js
  function invokeScript (line 9) | function invokeScript() {

FILE: basics/extendmodel.js
  class Attribute (line 5) | class Attribute {
    method constructor (line 6) | constructor(Process, Name, Value) {
    method toString (line 12) | toString() {
  class Attributes (line 20) | class Attributes {
    method constructor (line 21) | constructor() {
    method push (line 25) | push(Attr) {
    method toString (line 35) | toString() {
  method [Symbol.iterator] (line 29) | *[Symbol.iterator]() {
  class Sub (line 40) | class Sub {
    method constructor (line 41) | constructor(Process) {
    method SubFoo (line 45) | get SubFoo() {
    method SubBar (line 49) | get SubBar() {
    method Attributes (line 53) | get Attributes() {
    method toString (line 60) | toString() {
  class DiaryOfAReverseEngineer (line 65) | class DiaryOfAReverseEngineer {
    method constructor (line 66) | constructor(Process) {
    method Foo (line 70) | get Foo() {
    method Bar (line 74) | get Bar() {
    method Add (line 78) | Add(a, b) {
    method Sub (line 82) | get Sub() {
    method toString (line 86) | toString() {
  class ProcessModelParent (line 91) | class ProcessModelParent {
    method DiaryOfAReverseEngineer (line 92) | get DiaryOfAReverseEngineer() {
  function initializeScript (line 97) | function initializeScript() {

FILE: basics/extendmodel_1.js
  class ProcessModelParent (line 5) | class ProcessModelParent {
    method DiaryOfAReverseEngineer (line 6) | get DiaryOfAReverseEngineer() {
  function initializeScript (line 11) | function initializeScript() {

FILE: basics/extendmodel_2.js
  class DiaryOfAReverseEngineer (line 5) | class DiaryOfAReverseEngineer {
    method constructor (line 6) | constructor(Process) {
    method Foo (line 10) | get Foo() {
    method Bar (line 14) | get Bar() {
    method Add (line 18) | Add(a, b) {
  class ProcessModelParent (line 23) | class ProcessModelParent {
    method DiaryOfAReverseEngineer (line 24) | get DiaryOfAReverseEngineer() {
  function initializeScript (line 29) | function initializeScript() {

FILE: basics/extendmodel_2_1.js
  class DiaryOfAReverseEngineer (line 5) | class DiaryOfAReverseEngineer {
    method constructor (line 6) | constructor(Process) {
    method Foo (line 10) | get Foo() {
    method Bar (line 14) | get Bar() {
    method Add (line 18) | Add(a, b) {
    method toString (line 22) | toString() {
  class ProcessModelParent (line 27) | class ProcessModelParent {
    method DiaryOfAReverseEngineer (line 28) | get DiaryOfAReverseEngineer() {
  function initializeScript (line 33) | function initializeScript() {

FILE: basics/readmemory.js
  function read_u64 (line 9) | function read_u64(addr) {
  function invokeScript (line 13) | function invokeScript() {

FILE: codecov/codecov.js
  function ExtractModuleName (line 24) | function ExtractModuleName(ModulePath) {
  function CodeCoverageModule (line 30) | function CodeCoverageModule(Module) {
  function CodeCov (line 55) | function CodeCov(ModulePattern) {
  function initializeScript (line 136) | function initializeScript() {

FILE: gdt/gdt.js
  class GdtEntry (line 65) | class GdtEntry {
    method constructor (line 66) | constructor(Addr) {
    method toString (line 99) | toString() {
  function GetGdt (line 118) | function GetGdt() {
  function DumpGdtEntry (line 125) | function DumpGdtEntry(Addr) {
  function DumpAllGdt (line 129) | function DumpAllGdt() {
  function DumpGdt (line 142) | function DumpGdt(Selector) {
  function Gdt (line 151) | function Gdt(Selector) {
  function initializeScript (line 172) | function initializeScript() {

FILE: parse_eh_win64/parse_eh_win64.js
  function read_u32 (line 10) | function read_u32(addr) {
  function read_u8 (line 14) | function read_u8(addr) {
  class ScopeRecord (line 18) | class ScopeRecord {
    method constructor (line 24) | constructor(ScopeRecordAddress) {
    method IsTryFinally (line 31) | get IsTryFinally() {
    method HasFilter (line 35) | get HasFilter() {
    method InBound (line 40) | InBound(Begin, End) {
    method toString (line 45) | toString() {
  class Function (line 67) | class Function {
    method constructor (line 68) | constructor(BaseAddress, EHHandler, Begin, End) {
    method SetRecords (line 79) | SetRecords(Records, KeepRVA = false) {
    method toString (line 97) | toString() {
  function ParseCSpecificHandlerDatas (line 104) | function ParseCSpecificHandlerDatas(
  function ExtractExceptionHandlersForModule (line 124) | function ExtractExceptionHandlersForModule(
  class ModelExceptionHandlers (line 217) | class ModelExceptionHandlers {
    method constructor (line 218) | constructor(Type, Inst) {
    method __ExceptionHandlers (line 233) | __ExceptionHandlers(Modules) {
    method toString (line 259) | toString() {
  method [Symbol.iterator] (line 244) | *[Symbol.iterator]() {
  class ModelFunctions (line 264) | class ModelFunctions {
    method constructor (line 265) | constructor(Type, Inst) {
    method __Functions (line 276) | __Functions(Modules) {
    method toString (line 301) | toString() {
  method [Symbol.iterator] (line 285) | *[Symbol.iterator]() {
  class __ProcessModelExtension (line 306) | class __ProcessModelExtension {
    method ExceptionHandlers (line 307) | get ExceptionHandlers() {
    method Functions (line 311) | get Functions() {
  class __ModuleModelExtension (line 316) | class __ModuleModelExtension {
    method ExceptionHandlers (line 317) | get ExceptionHandlers() {
    method Functions (line 321) | get Functions() {
  function BangEHHandlers (line 326) | function BangEHHandlers() {
  function initializeScript (line 390) | function initializeScript() {

FILE: policybuffer/policybuffer.js
  function ReadShort (line 15) | function ReadShort(Address) {
  function ReadDword (line 27) | function ReadDword(Address) {
  function ReadQword (line 39) | function ReadQword(Address) {
  function initializeScript (line 51) | function initializeScript() {
  constant OP_ALWAYS_FALSE (line 106) | const OP_ALWAYS_FALSE = 0;
  constant OP_ALWAYS_TRUE (line 107) | const OP_ALWAYS_TRUE = 1;
  constant OP_NUMBER_MATCH (line 108) | const OP_NUMBER_MATCH = 2;
  constant OP_NUMBER_MATCH_RANGE (line 109) | const OP_NUMBER_MATCH_RANGE = 3;
  constant OP_NUMBER_AND_MATCH (line 110) | const OP_NUMBER_AND_MATCH = 4;
  constant OP_WSTRING_MATCH (line 111) | const OP_WSTRING_MATCH = 5;
  constant OP_ACTION (line 112) | const OP_ACTION = 6;
  function DisassPolicyBuffer (line 229) | function DisassPolicyBuffer(PolicyBufferAddress, PolicyType) {
  function initializeScript (line 315) | function initializeScript() {

FILE: sm/sm.js
  constant JSVAL_TAG_SHIFT (line 23) | const JSVAL_TAG_SHIFT = host.Int64(47);
  constant JSVAL_PAYLOAD_MASK (line 24) | const JSVAL_PAYLOAD_MASK = host.Int64(1).bitwiseShiftLeft(JSVAL_TAG_SHIF...
  constant CLASS_NON_NATIVE (line 25) | const CLASS_NON_NATIVE = host.Int64(0x40000);
  constant FLAG_DELEGATE (line 26) | const FLAG_DELEGATE = host.Int64(8);
  constant JSVAL_TYPE_DOUBLE (line 28) | const JSVAL_TYPE_DOUBLE = host.Int64(0x1fff0);
  constant JSVAL_TYPE_INT32 (line 29) | const JSVAL_TYPE_INT32 = host.Int64(0x1fff1);
  constant JSVAL_TYPE_BOOLEAN (line 30) | const JSVAL_TYPE_BOOLEAN = host.Int64(0x1fff2);
  constant JSVAL_TYPE_UNDEFINED (line 31) | const JSVAL_TYPE_UNDEFINED = host.Int64(0x1fff3);
  constant JSVAL_TYPE_NULL (line 32) | const JSVAL_TYPE_NULL = host.Int64(0x1fff4);
  constant JSVAL_TYPE_MAGIC (line 33) | const JSVAL_TYPE_MAGIC = host.Int64(0x1fff5);
  constant JSVAL_TYPE_STRING (line 34) | const JSVAL_TYPE_STRING = host.Int64(0x1fff6);
  constant JSVAL_TYPE_SYMBOL (line 35) | const JSVAL_TYPE_SYMBOL = host.Int64(0x1fff7);
  constant JSVAL_TYPE_OBJECT (line 36) | const JSVAL_TYPE_OBJECT = host.Int64(0x1fffc);
  constant INLINE_CHARS_BIT (line 38) | const INLINE_CHARS_BIT = host.Int64(1 << 6);
  constant LATIN1_CHARS_BIT (line 39) | const LATIN1_CHARS_BIT = host.Int64(1 << 9);
  constant JSID_TYPE_MASK (line 41) | const JSID_TYPE_MASK = host.Int64(0x7);
  constant JSID_TYPE_STRING (line 42) | const JSID_TYPE_STRING = host.Int64(0x0);
  constant JSID_TYPE_INT (line 43) | const JSID_TYPE_INT = host.Int64(0x1);
  constant JSID_TYPE_VOID (line 44) | const JSID_TYPE_VOID = host.Int64(0x2);
  constant JSID_TYPE_SYMBOL (line 45) | const JSID_TYPE_SYMBOL = host.Int64(0x4);
  constant SLOT_MASK (line 47) | const SLOT_MASK = host.Int64(0xffffff);
  constant FIXED_SLOTS_SHIFT (line 48) | const FIXED_SLOTS_SHIFT = host.Int64(24);
  function read_u64 (line 90) | function read_u64(Addr) {
  function heapslot_to_objectelements (line 98) | function heapslot_to_objectelements(Addr) {
  function printable (line 116) | function printable(Byte) {
  function byte_to_str (line 124) | function byte_to_str(Byte) {
  function jsid_is_int (line 136) | function jsid_is_int(Propid) {
  function jsid_is_string (line 145) | function jsid_is_string(Propid) {
  function get_property_from_shape (line 155) | function get_property_from_shape(Shape) {
  function jsvalue_to_instance (line 169) | function jsvalue_to_instance(Addr) {
  class __JSMagic (line 180) | class __JSMagic {
    method constructor (line 181) | constructor(Addr) {
    method toString (line 185) | toString() {
    method Logger (line 189) | Logger(Content) {
    method Display (line 193) | Display() {
  class __JSArgument (line 198) | class __JSArgument {
    method constructor (line 223) | constructor(Addr) {
    method toString (line 248) | toString() {
    method Logger (line 252) | Logger(Content) {
    method Display (line 256) | Display() {
  class __JSNull (line 262) | class __JSNull {
    method constructor (line 263) | constructor(Addr) {
    method toString (line 267) | toString() {
    method Logger (line 271) | Logger(Content) {
    method Display (line 275) | Display() {
  class __JSUndefined (line 280) | class __JSUndefined {
    method constructor (line 281) | constructor(Addr) {
    method toString (line 285) | toString() {
    method Logger (line 289) | Logger(Content) {
    method Display (line 293) | Display() {
  class __JSBoolean (line 298) | class __JSBoolean {
    method constructor (line 299) | constructor(Addr) {
    method toString (line 304) | toString() {
    method Logger (line 308) | Logger(Content) {
    method Display (line 312) | Display() {
  class __JSInt32 (line 317) | class __JSInt32 {
    method constructor (line 318) | constructor(Addr) {
    method toString (line 323) | toString() {
    method Logger (line 327) | Logger(Content) {
    method Display (line 331) | Display() {
  class __JSString (line 336) | class __JSString {
    method constructor (line 337) | constructor(Addr) {
    method toString (line 388) | toString() {
    method Logger (line 392) | Logger(Content) {
    method Display (line 396) | Display() {
  class __JSValue (line 401) | class __JSValue {
    method constructor (line 402) | constructor(Addr) {
    method Payload (line 409) | get Payload() {
    method Tag (line 417) | get Tag() {
  class __JSArray (line 426) | class __JSArray {
    method constructor (line 427) | constructor(Addr) {
    method toString (line 448) | toString() {
    method Logger (line 461) | Logger(Content) {
    method Display (line 465) | Display() {
  class __JSFunction (line 474) | class __JSFunction {
    method constructor (line 475) | constructor(Addr) {
    method toString (line 492) | toString() {
    method Flags (line 496) | get Flags() {
    method Logger (line 509) | Logger(Content) {
    method Display (line 513) | Display() {
  class __JSSymbol (line 519) | class __JSSymbol {
    method constructor (line 520) | constructor(Addr) {
    method toString (line 528) | toString() {
    method Logger (line 533) | Logger(Content) {
    method Display (line 537) | Display() {
  class __JSArrayBuffer (line 542) | class __JSArrayBuffer {
    method constructor (line 543) | constructor(Addr) {
    method Flags (line 563) | get Flags() {
    method ByteLength (line 625) | get ByteLength() {
    method toString (line 629) | toString() {
    method Logger (line 633) | Logger(Content) {
    method Display (line 637) | Display() {
  class __JSTypedArray (line 644) | class __JSTypedArray {
    method constructor (line 645) | constructor(Addr) {
    method Type (line 681) | get Type() {
    method ByteOffset (line 685) | get ByteOffset() {
    method ByteLength (line 689) | get ByteLength() {
    method Length (line 693) | get Length() {
    method toString (line 697) | toString() {
    method Logger (line 701) | Logger(Content) {
    method Display (line 705) | Display() {
  class __JSMap (line 714) | class __JSMap {
    method constructor (line 715) | constructor(Addr) {
    method toString (line 720) | toString() {
    method Logger (line 724) | Logger(Content) {
    method Display (line 728) | Display() {
  class __JSDouble (line 733) | class __JSDouble {
    method constructor (line 734) | constructor(Addr) {
    method toString (line 738) | toString() {
    method Logger (line 747) | Logger(Content) {
    method Display (line 751) | Display() {
  class __JSObject (line 782) | class __JSObject {
    method constructor (line 809) | constructor(Addr) {
    method Properties (line 886) | get Properties() {
    method ClassName (line 890) | get ClassName() {
    method toString (line 894) | toString() {
    method Logger (line 915) | Logger(Content) {
    method Display (line 919) | Display() {
  function smdump_jsobject (line 936) | function smdump_jsobject(Addr, Type = null) {
  function smdump_jsvalue (line 960) | function smdump_jsvalue(Addr) {
  function init (line 984) | function init() {
  function ion_insertbp (line 1002) | function ion_insertbp() {
  function dump_nursery_stats (line 1026) | function dump_nursery_stats(Nursery) {
  function in_nursery (line 1054) | function in_nursery(Addr) {
  function initializeScript (line 1136) | function initializeScript() {

FILE: telescope/telescope.js
  function ReadU64 (line 25) | function ReadU64(Addr) {
  function ReadU32 (line 37) | function ReadU32(Addr) {
  function ReadU16 (line 49) | function ReadU16(Addr) {
  function ReadString (line 61) | function ReadString(Addr, MaxLength) {
  function ReadWideString (line 76) | function ReadWideString(Addr) {
  function Disassemble (line 86) | function Disassemble(Addr) {
  function FormatU64 (line 109) | function FormatU64(Addr) {
  function FormatU32 (line 113) | function FormatU32(Addr) {
  function FormatString (line 117) | function FormatString(Str) {
  function BitSet (line 125) | function BitSet(Value, Bit) {
  function HandleTTD (line 230) | function HandleTTD() {
  function HandleUser (line 297) | function HandleUser() {
  function HandleKernel (line 411) | function HandleKernel() {
  function InitializeVASpace (line 460) | function InitializeVASpace() {
  function InitializeWrapper (line 479) | function InitializeWrapper(Funct) {
  class _Region (line 516) | class _Region {
    method constructor (line 517) | constructor(BaseAddress, Size, Name, Properties) {
    method In (line 540) | In(Addr) {
    method toString (line 546) | toString() {
  function AddressToRegion (line 557) | function AddressToRegion(Addr) {
  class _ChainEntry (line 583) | class _ChainEntry {
    method constructor (line 584) | constructor(Addr, Value) {
    method Equals (line 603) | Equals(Entry) {
    method toString (line 607) | toString() {
  class _Chain (line 666) | class _Chain {
    method constructor (line 667) | constructor(Addr) {
    method FollowPtr (line 683) | FollowPtr() {
    method toString (line 728) | toString() {
  method [Symbol.iterator] (line 750) | *[Symbol.iterator]() {
  function CreateChain (line 757) | function CreateChain(Addr) {
  function Telescope (line 770) | function Telescope(Addr) {
  function initializeScript (line 798) | function initializeScript() {
Condensed preview — 26 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (133K chars).
[
  {
    "path": "LICENSE",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) 2019 Axel Souchet\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "Manifest/Manifest.1.xml",
    "chars": 4570,
    "preview": "<?xml version='1.0' encoding='utf-8'?>\n<ExtensionPackages Version='1.0.0.0' Compression='none'>\n    <ExtensionPackage>\n "
  },
  {
    "path": "Manifest/ManifestVersion.txt",
    "chars": 12,
    "preview": "1\n1.0.0.0\n1\n"
  },
  {
    "path": "Manifest/config.xml",
    "chars": 662,
    "preview": "<?xml version='1.0' encoding='utf-8'?>\n<Settings Version='1'>\n    <Namespace Name='Extensions'>\n        <Setting Name='E"
  },
  {
    "path": "README.md",
    "chars": 2840,
    "preview": "# windbg-scripts\n\n`windbg-scripts` is a collection of [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/dri"
  },
  {
    "path": "basics/Int64.js",
    "chars": 614,
    "preview": "// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet logln = function (e) {\n    host.diagnostics.debugLog(e + '\\n'"
  },
  {
    "path": "basics/breakpoint.js",
    "chars": 1220,
    "preview": "// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet logln = function (e) {\n    host.diagnostics.debugLog(e + '\\n'"
  },
  {
    "path": "basics/breakpoint2.js",
    "chars": 1359,
    "preview": "// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet logln = function (e) {\n    host.diagnostics.debugLog(e + '\\n'"
  },
  {
    "path": "basics/eval.js",
    "chars": 303,
    "preview": "// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet logln = function (e) {\n    host.diagnostics.debugLog(e + '\\n'"
  },
  {
    "path": "basics/extendmodel.js",
    "chars": 1895,
    "preview": "// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nclass Attribute {\n    constructor(Process, Name, Value) {\n       "
  },
  {
    "path": "basics/extendmodel_1.js",
    "chars": 309,
    "preview": "// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nclass ProcessModelParent {\n    get DiaryOfAReverseEngineer() {\n  "
  },
  {
    "path": "basics/extendmodel_2.js",
    "chars": 604,
    "preview": "// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nclass DiaryOfAReverseEngineer {\n    constructor(Process) {\n      "
  },
  {
    "path": "basics/extendmodel_2_1.js",
    "chars": 680,
    "preview": "// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nclass DiaryOfAReverseEngineer {\n    constructor(Process) {\n      "
  },
  {
    "path": "basics/readmemory.js",
    "chars": 681,
    "preview": "// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet logln = function (e) {\n    host.diagnostics.debugLog(e + '\\n'"
  },
  {
    "path": "codecov/README.md",
    "chars": 1507,
    "preview": "# codecov.js\n\n`codecov.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascri"
  },
  {
    "path": "codecov/codecov.js",
    "chars": 3988,
    "preview": "// Axel '0vercl0k' Souchet - 2 Feb 2018\n//\n// Example:\n//   0:000> !codecov \"kernel\"\n//   Looking for *kernel*..\n//   Fo"
  },
  {
    "path": "gdt/README.md",
    "chars": 5101,
    "preview": "# gdt.js\n\n`gdt.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debug"
  },
  {
    "path": "gdt/gdt.js",
    "chars": 6522,
    "preview": "// Axel '0vercl0k' Souchet - October 8 2021\n\n'use strict';\n\n//\n// Small reminders from the manual intels on segments in "
  },
  {
    "path": "parse_eh_win64/README.md",
    "chars": 4080,
    "preview": "# parse_eh_win64.js\n\n`parse_eh_win64.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/deb"
  },
  {
    "path": "parse_eh_win64/parse_eh_win64.js",
    "chars": 12776,
    "preview": "// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet log = host.diagnostics.debugLog;\nlet logln = function (e) {\n "
  },
  {
    "path": "policybuffer/README.md",
    "chars": 2770,
    "preview": "# policybuffer.js\n\n`policybuffer.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugge"
  },
  {
    "path": "policybuffer/policybuffer.js",
    "chars": 10374,
    "preview": "// Axel '0vercl0k' Souchet - 5 March 2019\n// sandbox::PolicyBase::EvalPolicy\n\n'use strict';\n\n//\n// Utility functions.\n/"
  },
  {
    "path": "sm/README.md",
    "chars": 4213,
    "preview": "# sm.js\n\n`sm.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debugge"
  },
  {
    "path": "sm/sm.js",
    "chars": 34020,
    "preview": "// Axel '0vercl0k' Souchet - 24-June-2018\n//\n// Example:\n//   * from the interpreter:\n//       Math.atan2(['short', 13."
  },
  {
    "path": "telescope/README.md",
    "chars": 4973,
    "preview": "# telescope.js\n\n`telescope.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/java"
  },
  {
    "path": "telescope/telescope.js",
    "chars": 20502,
    "preview": "// Axel '0vercl0k' Souchet - 7th December 2018\n\n'use strict';\n\nconst log = host.diagnostics.debugLog;\nconst logln = p =>"
  }
]

About this extraction

This page contains the full source code of the 0vercl0k/windbg-scripts GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 26 files (124.7 KB), approximately 34.8k tokens, and a symbol index with 267 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!