[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Axel Souchet\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Manifest/Manifest.1.xml",
    "content": "<?xml version='1.0' encoding='utf-8'?>\n<ExtensionPackages Version='1.0.0.0' Compression='none'>\n    <ExtensionPackage>\n        <Name>Telescope</Name>\n        <Version>1.0.0.1</Version>\n        <Description>Telescope data dereference</Description>\n        <Components>\n            <ScriptComponent Name='Telescope' Type='Engine' File='..\\telescope\\telescope.js' FilePathKind='RepositoryRelative'>\n                <FunctionAliases>\n                    <FunctionAlias Name='telescope'>\n                        <AliasItem>\n                            <Syntax>\n                                <![CDATA[!telescope <address>]]>\n                            </Syntax>\n                            <Description>\n                                <![CDATA[Telescope dereference.]]>\n                            </Description>\n                        </AliasItem>\n                    </FunctionAlias>\n                </FunctionAliases>\n            </ScriptComponent>\n        </Components>\n    </ExtensionPackage>\n\n    <ExtensionPackage>\n        <Name>Gdt</Name>\n        <Version>1.0.0.1</Version>\n        <Description>Gdt dump</Description>\n        <Components>\n            <ScriptComponent Name='Gdt' Type='Engine' File='..\\gdt\\gdt.js' FilePathKind='RepositoryRelative'>\n                <FunctionAliases>\n                    <FunctionAlias Name='gdt'>\n                        <AliasItem>\n                            <Syntax>\n                                <![CDATA[!gdt <segment selector>]]>\n                            </Syntax>\n                            <Description>\n                                <![CDATA[Dump the GDT or a GDT entry.]]>\n                            </Description>\n                        </AliasItem>\n                    </FunctionAlias>\n                </FunctionAliases>\n            </ScriptComponent>\n        </Components>\n    </ExtensionPackage>\n\n    <ExtensionPackage>\n        <Name>sm</Name>\n        <Version>1.0.0.1</Version>\n        <Description>Pretty printers for JS::Value and JSObject objects in SpiderMonkey</Description>\n        <Components>\n            <ScriptComponent Name='sm' Type='Engine' File='..\\sm\\sm.js' FilePathKind='RepositoryRelative'>\n                <LoadTriggers>\n                    <TriggerSet>\n                        <ModuleTrigger Name='xul.dll' />\n                    </TriggerSet>\n                    <TriggerSet>\n                        <ModuleTrigger Name='js.exe' />\n                    </TriggerSet>\n                </LoadTriggers>\n                <FunctionAliases>\n                    <FunctionAlias Name='smdump_jsvalue'>\n                        <AliasItem>\n                            <Syntax>\n                                <![CDATA[!smdump_jsvalue <address>]]>\n                            </Syntax>\n                            <Description>\n                                <![CDATA[Pretty print a JS::Value object.]]>\n                            </Description>\n                        </AliasItem>\n                    </FunctionAlias>\n                    <FunctionAlias Name='smdump_jsobject'>\n                        <AliasItem>\n                            <Syntax>\n                                <![CDATA[!smdump_jsobject <address>]]>\n                            </Syntax>\n                            <Description>\n                                <![CDATA[Pretty print a JSObject object.]]>\n                            </Description>\n                        </AliasItem>\n                    </FunctionAlias>\n                    <FunctionAlias Name='ion_insertbp'>\n                        <AliasItem>\n                            <Syntax>\n                                <![CDATA[!ion_insertbp]]>\n                            </Syntax>\n                            <Description>\n                                <![CDATA[Insert an int3 in the assembly buffer to debug JIT'd code.]]>\n                            </Description>\n                        </AliasItem>\n                    </FunctionAlias>\n                    <FunctionAlias Name='in_nursery'>\n                        <AliasItem>\n                            <Syntax>\n                                <![CDATA[!in_nursery]]>\n                            </Syntax>\n                            <Description>\n                                <![CDATA[Figure out if an object is stored in the Nursery or not.]]>\n                            </Description>\n                        </AliasItem>\n                    </FunctionAlias>\n\n                </FunctionAliases>\n            </ScriptComponent>\n        </Components>\n    </ExtensionPackage>\n</ExtensionPackages>\n"
  },
  {
    "path": "Manifest/ManifestVersion.txt",
    "content": "1\n1.0.0.0\n1\n"
  },
  {
    "path": "Manifest/config.xml",
    "content": "<?xml version='1.0' encoding='utf-8'?>\n<Settings Version='1'>\n    <Namespace Name='Extensions'>\n        <Setting Name='ExtensionRepository' Type='VT_BSTR' Value='Implicit'></Setting>\n        <Namespace Name='ExtensionRepositories'>\n            <Namespace Name='overgallery'>\n                <Setting Name='Id' Type='VT_BSTR' Value='690F3350-53EE-4EC4-A95B-750068897ED4'></Setting>\n                <Setting Name='LocalCacheRootFolder' Type='VT_BSTR' Value='c:\\work\\codes\\windbg-scripts\\Manifest'></Setting>\n                <Setting Name='IsEnabled' Type='VT_BOOL' Value='true'></Setting>\n            </Namespace>\n        </Namespace>\n    </Namespace>\n</Settings>\n"
  },
  {
    "path": "README.md",
    "content": "# windbg-scripts\n\n`windbg-scripts` is a collection of [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debugger-scripting) debugger extensions  for WinDbg.\n\n* [basics](basics/): various examples of basic usage of various APIs,\n* [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/)),\n* [telescope](telescope/): [telescope](https://gef.readthedocs.io/en/latest/commands/dereference/) like command for WinDbg,\n* [sm](sm/): pretty-printing of Spidermonkey `js::Value` and `JSObject` objects,\n* [codecov](codecov/): extract code-coverage out of a [TTD](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/time-travel-debugging-overview) trace,\n* [policybuffer](policybuffer/): disassemble a Chrome policy buffer program,\n* [gdt](gdt/): dump the [Global Descriptor Table](https://wiki.osdev.org/Global_Descriptor_Table).\n\n## Installing the script gallery\n\nIf you would like to have `telescope` and `sm` loaded every time your debugger starts instead of loading the extensions manually follow the below steps:\n\n1. Clone this GitHub repository,\n\n2. Edit the `Manifest\\config.xml` file and update the `LocalCacheRootFolder` path with a value that makes sense,\n\n3. Open the debugger and import the gallery by running `.settings load c:\\path\\where\\cloned\\windbg-scripts\\Manifest\\config.xml` and `.settings save`.\n\n4. Restart the debugger and you should be able to run `!telescope` as well as inspecting the gallery content from the data-model.\n\n   ```text\n   0:000> dx -r1 Debugger.State.ExtensionGallery.ExtensionRepositories\n   Debugger.State.ExtensionGallery.ExtensionRepositories                \n       [0x0]            : overgallery\n       [0x1]            : LocalInstalled\n   \n   0:000> dx -r1 Debugger.State.ExtensionGallery.ExtensionRepositories[0]\n   Debugger.State.ExtensionGallery.ExtensionRepositories[0]                 : overgallery\n       Name             : overgallery\n       ManifestVersion  : 0x1\n       URL             \n       Enabled          : true\n       Packages        \n   \n   0:000> dx -r1 Debugger.State.ExtensionGallery.ExtensionRepositories[0].Packages\n   Debugger.State.ExtensionGallery.ExtensionRepositories[0].Packages                \n       [0x0]            : Telescope\n   \n   0:000> dx -r1 Debugger.State.ExtensionGallery.ExtensionRepositories[0].Packages[0]\n   Debugger.State.ExtensionGallery.ExtensionRepositories[0].Packages[0]                 : Telescope\n       Name             : Telescope\n       Version          : 1.0.0.1\n       Description      : Telescope data dereference\n       Size             : 0\n       IsDownloaded     : true\n       Components      \n   ```\n"
  },
  {
    "path": "basics/Int64.js",
    "content": "﻿// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet logln = function (e) {\n    host.diagnostics.debugLog(e + '\\n');\n}\n\nfunction invokeScript() {\n    let a = host.Int64(1337);\n    let aplusone = a + 1;\n    logln(aplusone.toString(16));\n    let b = host.parseInt64('0xdeadbeefbaadc0de', 16);\n    let bplusone = b.add(1);\n    logln(bplusone.toString(16));\n    let bplusonenothrow = b.convertToNumber() + 1;\n    logln(bplusonenothrow);\n    try {\n        let bplusonethrow = b + 1;\n    } catch(e) {\n        logln(e);\n    }\n    logln(a.compareTo(1));\n    logln(a.compareTo(1337));\n    logln(a.compareTo(1338));\n}\n"
  },
  {
    "path": "basics/breakpoint.js",
    "content": "﻿// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet logln = function (e) {\n    host.diagnostics.debugLog(e + '\\n');\n}\n\nfunction handle_bp() {\n    let Regs = host.currentThread.Registers.User;\n    let Args = [Regs.rcx, Regs.rdx, Regs.r8];\n    let ArgsS = Args.map(c => c.toString(16));\n    let HeapHandle = ArgsS[0];\n    let Flags = ArgsS[1];\n    let Size = ArgsS[2];\n    logln('RtlAllocateHeap: HeapHandle: ' + HeapHandle + ', Flags: ' + Flags + ', Size: ' + Size);\n}\n\nfunction invokeScript() {\n    let Control = host.namespace.Debugger.Utility.Control;\n    let Regs = host.currentThread.Registers.User;\n    let CurrentProcess = host.currentProcess;\n    let BreakpointAlreadySet = CurrentProcess.Debug.Breakpoints.Any(\n        c => c.OffsetExpression == 'ntdll!RtlAllocateHeap+0x0'\n    );\n\n    if(BreakpointAlreadySet == false) {\n        let Bp = Control.SetBreakpointAtOffset('RtlAllocateHeap', 0, 'ntdll');\n        Bp.Command = '.echo doare; dx @$scriptContents.handle_bp(); gc';\n    } else {\n        logln('Breakpoint already set.');\n    }\n    logln('Press \"g\" to run the target.');\n    // let Lines = Control.ExecuteCommand('gc');\n    // for(let Line of Lines) {\n    //     logln('Line: ' + Line);\n    // }\n}\n"
  },
  {
    "path": "basics/breakpoint2.js",
    "content": "﻿// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet logln = function (e) {\n    host.diagnostics.debugLog(e + '\\n');\n}\n\nfunction handle_bp() {\n    let Regs = host.currentThread.Registers.User;\n    let Args = [Regs.rcx, Regs.rdx, Regs.r8];\n    let ArgsS = Args.map(c => c.toString(16));\n    let HeapHandle = ArgsS[0];\n    let Flags = ArgsS[1];\n    let Size = ArgsS[2];\n    logln('RtlAllocateHeap: HeapHandle: ' + HeapHandle + ', Flags: ' + Flags + ', Size: ' + Size);\n    if(Args[2].compareTo(0x100) > 0) {\n        // stop execution if the allocation size is bigger than 0x100\n        return true;\n    }\n    // keep the execution going if it's a small size\n    return false;\n}\n\nfunction invokeScript() {\n    let Control = host.namespace.Debugger.Utility.Control;\n    let Regs = host.currentThread.Registers.User;\n    let CurrentProcess = host.currentProcess;\n    let HeapAlloc = host.getModuleSymbolAddress('ntdll', 'RtlAllocateHeap');\n    let BreakpointAlreadySet = CurrentProcess.Debug.Breakpoints.Any(\n        c => c.Address == HeapAlloc\n    );\n    if(BreakpointAlreadySet == false) {\n        logln('RltAllocateHeap @ ' + HeapAlloc.toString(16));\n        Control.ExecuteCommand('bp /w \"@$scriptContents.handle_bp()\" ' + HeapAlloc.toString(16));\n    } else {\n        logln('Breakpoint already set.');\n    }\n    logln('Press \"g\" to run the target.');\n}\n"
  },
  {
    "path": "basics/eval.js",
    "content": "﻿// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet logln = function (e) {\n    host.diagnostics.debugLog(e + '\\n');\n}\n\nfunction invokeScript() {\n    let Control = host.namespace.Debugger.Utility.Control;\n    for(let Line of Control.ExecuteCommand('kp')) {\n        logln('Line: ' + Line);\n    }\n}\n"
  },
  {
    "path": "basics/extendmodel.js",
    "content": "﻿// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nclass Attribute {\n    constructor(Process, Name, Value) {\n        this.__process = Process;\n        this.Name = Name;\n        this.Value = Value;\n    }\n\n    toString() {\n        let S = 'Process: ' + this.__process.Name + ', ';\n        S += 'Name: ' + this.Name + ', ';\n        S += 'Value: ' + this.Value;\n        return S;\n    }\n}\n\nclass Attributes {\n    constructor() {\n        this.__attrs = [];\n    }\n\n    push(Attr) {\n        this.__attrs.push(Attr);\n    }\n\n    *[Symbol.iterator]() {\n        for (let Attr of this.__attrs) {\n            yield Attr;\n        }\n    }\n\n    toString() {\n        return 'Attributes';\n    }\n}\n\nclass Sub {\n    constructor(Process) {\n        this.__process = Process;\n    }\n\n    get SubFoo() {\n        return 'SubFoo from ' + this.__process.Name;\n    }\n\n    get SubBar() {\n        return 'SubBar from ' + this.__process.Name;\n    }\n\n    get Attributes() {\n        let Attrs = new Attributes();\n        Attrs.push(new Attribute(this.__process, 'attr0', 'value0'));\n        Attrs.push(new Attribute(this.__process, 'attr1', 'value0'));\n        return Attrs;\n    }\n\n    toString() {\n        return 'Sub module';\n    }\n}\n\nclass DiaryOfAReverseEngineer {\n    constructor(Process) {\n        this.__process = Process;\n    }\n\n    get Foo() {\n        return 'Foo from ' + this.__process.Name;\n    }\n\n    get Bar() {\n        return 'Bar from ' + this.__process.Name;\n    }\n\n    Add(a, b) {\n        return a + b;\n    }\n\n    get Sub() {\n        return new Sub(this.__process);\n    }\n\n    toString() {\n        return 'Diary of a reverse-engineer';\n    }\n}\n\nclass ProcessModelParent {\n    get DiaryOfAReverseEngineer() {\n        return new DiaryOfAReverseEngineer(this);\n    }\n}\n\nfunction initializeScript() {\n    return [new host.namedModelParent(\n        ProcessModelParent,\n        'Debugger.Models.Process'\n    )];\n}\n"
  },
  {
    "path": "basics/extendmodel_1.js",
    "content": "﻿// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nclass ProcessModelParent {\n    get DiaryOfAReverseEngineer() {\n        return 'hello from ' + this.Name;\n    }\n}\n\nfunction initializeScript() {\n    return [new host.namedModelParent(\n        ProcessModelParent,\n        'Debugger.Models.Process'\n    )];\n}"
  },
  {
    "path": "basics/extendmodel_2.js",
    "content": "﻿// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nclass DiaryOfAReverseEngineer {\n    constructor(Process) {\n        this.process = Process;\n    }\n\n    get Foo() {\n        return 'Foo from ' + this.process.Name;\n    }\n\n    get Bar() {\n        return 'Bar from ' + this.process.Name;\n    }\n\n    Add(a, b) {\n        return a + b;\n    }\n}\n\nclass ProcessModelParent {\n    get DiaryOfAReverseEngineer() {\n        return new DiaryOfAReverseEngineer(this);\n    }\n}\n\nfunction initializeScript() {\n    return [new host.namedModelParent(\n        ProcessModelParent,\n        'Debugger.Models.Process'\n    )];\n}"
  },
  {
    "path": "basics/extendmodel_2_1.js",
    "content": "﻿// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nclass DiaryOfAReverseEngineer {\n    constructor(Process) {\n        this.__process = process;\n    }\n\n    get Foo() {\n        return 'Foo from ' + this.__process.Name;\n    }\n\n    get Bar() {\n        return 'Bar from ' + this.__process.Name;\n    }\n\n    Add(a, b) {\n        return a + b;\n    }\n\n    toString() {\n        return 'Diary of a reverse-engineer';\n    }\n}\n\nclass ProcessModelParent {\n    get DiaryOfAReverseEngineer() {\n        return new DiaryOfAReverseEngineer(this);\n    }\n}\n\nfunction initializeScript() {\n    return [new host.namedModelParent(\n        ProcessModelParent,\n        'Debugger.Models.Process'\n    )];\n}"
  },
  {
    "path": "basics/readmemory.js",
    "content": "﻿// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet logln = function (e) {\n    host.diagnostics.debugLog(e + '\\n');\n}\n\nfunction read_u64(addr) {\n    return host.memory.readMemoryValues(addr, 1, 8)[0];\n}\n\nfunction invokeScript() {\n    let Regs = host.currentThread.Registers.User;\n    let a = read_u64(Regs.rsp);\n    logln(a.toString(16));\n    try {\n        read_u64(0xdeadbeef);\n    } catch(e) {\n        logln(e);\n    }\n    let WideStr = host.currentProcess.Environment.EnvironmentBlock.ProcessParameters.ImagePathName.Buffer;\n    logln(host.memory.readWideString(WideStr));\n    let WideStrAddress = WideStr.address;\n    logln(host.memory.readWideString(WideStrAddress));\n}\n"
  },
  {
    "path": "codecov/README.md",
    "content": "# codecov.js\n\n`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.\n\nThe file looks like the below:\n\n```text\n; TracePath: C:\\work\\codes\\blazefox\\js01.run\n; c:\\windows\\system32\\kernelbase.dll, 7fffb4ce0000, 293000\nkernelbase.dll+5df40\nkernelbase.dll+5df43\nkernelbase.dll+5df47\nkernelbase.dll+5df4b\nkernelbase.dll+5df4f\n...\n; c:\\windows\\system32\\kernel32.dll, 7fffb6460000, b3000\nkernel32.dll+1f3a0\nkernel32.dll+21bb0\nkernel32.dll+1bb90\nkernel32.dll+1a280\nkernel32.dll+1a284\nkernel32.dll+1e640\nkernel32.dll+63a0\n```\n\n## Usage\n\nRun `.scriptload codecov.js` to load the script. You can extract code-coverage using `!codecov \"foo\"`.\n\n## Examples\n\nExtract code-coverage for every module having `kernel` in their name:\n\n```text\n0:000> !codecov \"kernel\"\nLooking for *kernel*..\nFound 2 hits\nFound 7815 unique addresses in C:\\WINDOWS\\System32\\KERNELBASE.dll\nFound 1260 unique addresses in C:\\WINDOWS\\System32\\KERNEL32.DLL\nWriting C:\\work\\codes\\tmp\\js01.run.kernel.text...\nDone!\n@$codecov(\"kernel\")\n\n0:000> !codecov \"kernel\"\nLooking for *kernel*..\nThe output file C:\\work\\codes\\tmp\\js01.run.kernel.text already exists, exiting.\n@$codecov(\"kernel\")\n```\n\n"
  },
  {
    "path": "codecov/codecov.js",
    "content": "// Axel '0vercl0k' Souchet - 2 Feb 2018\n//\n// Example:\n//   0:000> !codecov \"kernel\"\n//   Looking for *kernel*..\n//   Found 2 hits\n//   Found 7815 unique addresses in C:\\WINDOWS\\System32\\KERNELBASE.dll\n//   Found 1260 unique addresses in C:\\WINDOWS\\System32\\KERNEL32.DLL\n//   Writing C:\\work\\codes\\tmp\\js01.run.kernel.text...\n//   Done!\n//   @$codecov(\"kernel\")\n//   0:000> !codecov \"kernel\"\n//   Looking for *kernel*..\n//   The output file C:\\work\\codes\\tmp\\js01.run.kernel.text already exists, exiting.\n//   @$codecov(\"kernel\")\n//\n\n'use strict';\n\nconst log = host.diagnostics.debugLog;\nconst logln = p => host.diagnostics.debugLog(p + '\\n');\nconst hex = p => p.toString(16);\n\nfunction ExtractModuleName(ModulePath) {\n    return ModulePath.slice(\n        ModulePath.lastIndexOf('\\\\') + 1\n    );\n}\n\nfunction CodeCoverageModule(Module) {\n    const CurrentSession = host.currentSession;\n    const BaseAddress = Module.BaseAddress;\n    const Size = Module.Size;\n\n    const CoverageLines = CurrentSession.TTD.Memory(\n        BaseAddress,\n        BaseAddress.add(Size),\n        'EC'\n    );\n\n    const Offsets = Array.from(CoverageLines).map(\n        p => hex(\n            p.Address.subtract(BaseAddress)\n        )\n    );\n\n    return {\n        'Path' : Module.Name.toLowerCase(),\n        'Base' : BaseAddress,\n        'Size' : Size,\n        'Offsets' : Offsets\n    };\n}\n\nfunction CodeCov(ModulePattern) {\n    const CurrentSession = host.currentSession;\n    const CurrentProcess = host.currentProcess;\n    const Utility = host.namespace.Debugger.Utility;\n\n    if(!CurrentSession.Attributes.Target.IsTTDTarget) {\n        logln('!codecov expects a TTD trace');\n        return;\n    }\n\n    if(ModulePattern == undefined) {\n        logln('!codecov \"pattern\"');\n        return;\n    }\n\n    ModulePattern = ModulePattern.toLowerCase();\n    logln('Looking for *' + ModulePattern + '*..');\n    const Modules = CurrentProcess.Modules.Where(\n        p => p.Name.toLowerCase().indexOf(ModulePattern) != -1\n    );\n\n    if(Modules.Count() == 0) {\n        logln('Could not find any matching module, exiting');\n        return;\n    }\n\n    const TracePath = CurrentSession.Attributes.Target.Details.DumpFileName;\n    const TraceDir = TracePath.slice(\n        0,\n        TracePath.lastIndexOf('\\\\')\n    );\n    const TraceName = TracePath.slice(\n        TracePath.lastIndexOf('\\\\') + 1\n    );\n    const FilePath = TraceDir + '\\\\' + TraceName + '.' + ModulePattern + '.txt';\n    if(Utility.FileSystem.FileExists(FilePath)) {\n        logln('The output file ' + FilePath + ' already exists, exiting.');\n        return;\n    }\n\n    const Metadata = {\n        'TracePath' : TracePath\n    };\n\n    const CoverageModules = [];\n    logln('Found ' + Modules.Count() + ' hits');\n    for(const Module of Modules) {\n        const ModuleCoverage = CodeCoverageModule(Module);\n        logln('Found ' + ModuleCoverage.Offsets.length + ' unique addresses in ' + Module.Name);\n        CoverageModules.push(ModuleCoverage);\n    }\n\n    logln('Writing ' + FilePath + '...');\n    const FileHandle = Utility.FileSystem.CreateFile(FilePath, 'CreateAlways');\n    const Writer = Utility.FileSystem.CreateTextWriter(FileHandle);\n    for(const [Name, Value] of Object.entries(Metadata)) {\n        Writer.WriteLine('; ' + Name + ': ' + Value);\n    }\n\n    for(const Module of CoverageModules) {\n\n        //\n        // First write the metadata that is module specific.\n        //\n\n        Writer.WriteLine('; ' + Module.Path + ', ' + hex(Module.Base) + ', ' + hex(Module.Size));\n\n        //\n        // Write down the offsets.\n        //\n\n        const ModuleName = ExtractModuleName(Module.Path);\n        for(const Offset of Module.Offsets) {\n            Writer.WriteLine(ModuleName + '+' + hex(Offset));\n        }\n    }\n\n    FileHandle.Close();\n    logln('Done!');\n}\n\nfunction initializeScript() {\n    return [\n        new host.apiVersionSupport(1, 2),\n        new host.functionAlias(\n            CodeCov,\n            'codecov'\n        )\n    ];\n}\n"
  },
  {
    "path": "gdt/README.md",
    "content": "# gdt.js\n\n`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.\n\n\n## Usage\n\nRun `.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.\n\n## Examples\n\n* Dumping the GDT entry that enables [WoW64](https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details):\n```\n32.kd> !gdt @cs\ndt nt!_KGDTENTRY64 0xfffff8045215dfd0\n   Base: [0x0 -> 0xffffffff]\n   Type: Code Execute/Read Accessed (0xb)\n    DPL: 0x3\nPresent: 0x1\n   Mode: 32b Compat\n@$gdt(@cs)\n\n32.kd> dg @cs\n                                                    P Si Gr Pr Lo\nSel        Base              Limit          Type    l ze an es ng Flags\n---- ----------------- ----------------- ---------- - -- -- -- -- --------\n0023 00000000`00000000 00000000`ffffffff Code RE Ac 3 Bg Pg P  Nl 00000cfb\n```\n\n* Dumping the GDT entry that allows 32-bit code to invoke 64-bit code:\n```\n32.kd> !gdt 0x33\ndt nt!_KGDTENTRY64 0xfffff8045215dfe0\n   Base: [0x0 -> 0x0]\n   Type: Code Execute/Read Accessed (0xb)\n    DPL: 0x3\nPresent: 0x1\n   Mode: 64b\n@$gdt(0x33)\n\n32.kd> dg 33\n                                                    P Si Gr Pr Lo\nSel        Base              Limit          Type    l ze an es ng Flags\n---- ----------------- ----------------- ---------- - -- -- -- -- --------\n0033 00000000`00000000 00000000`00000000 Code RE Ac 3 Nb By P  Lo 000002fb\n```\n\n* Dumping the [Task State Segment](https://wiki.osdev.org/Task_State_Segment):\n```\n32.kd> !gdt @tr\ndt nt!_KGDTENTRY64 0xfffff8045215dff0\n   Base: [0xfffff8045215c000 -> 0xfffff8045215c067]\n   Type: TSS64 Busy (0xb)\n    DPL: 0x0\nPresent: 0x1\n@$gdt(@tr)\n\n32.kd> dg @tr\n                                                    P Si Gr Pr Lo\nSel        Base              Limit          Type    l ze an es ng Flags\n---- ----------------- ----------------- ---------- - -- -- -- -- --------\n0040 00000000`5215c000 00000000`00000067 TSS32 Busy 0 Nb By P  Nl 0000008b\n```\n\n* 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:\n```\n32.kd> !gdt @fs\ndt nt!_KGDTENTRY64 0xfffff8045215e000\n   Base: [0x326000 -> 0x329c00]\n   Type: Data Read/Write Accessed (0x3)\n    DPL: 0x3\nPresent: 0x1\n@$gdt(@fs)\n\n32.kd> !teb\nWow64 TEB32 at 0000000000326000\n    ExceptionList:        00000000004ff59c\n    StackBase:            0000000000500000\n    StackLimit:           00000000004f2000\n    SubSystemTib:         0000000000000000\n    FiberData:            0000000000001e00\n    ArbitraryUserPointer: 0000000000000000\n    Self:                 0000000000326000\n    EnvironmentPointer:   0000000000000000\n    ClientId:             0000000000001ad8 . 0000000000001adc\n    RpcHandle:            0000000000000000\n    Tls Storage:          0000000000834188\n    PEB Address:          0000000000323000\n    LastErrorValue:       0\n    LastStatusValue:      c000007c\n    Count Owned Locks:    0\n    HardErrorMode:        0\n```\n* Dumping the entire GDT on a Windows 10 64-bit Virtual Machine:\n```\n32.kd> !gdt\nDumping the GDT from 0xfffff8045215dfb0 to 0xfffff8045215e007..\n[0]: dt nt!_KGDTENTRY64 0xfffff8045215dfb0\n   Base: [0x0 -> 0x0]\n   Type: Reserved (0x0)\n    DPL: 0x0\nPresent: 0x0\n[1]: dt nt!_KGDTENTRY64 0xfffff8045215dfb8\n   Base: [0x0 -> 0x0]\n   Type: Reserved (0x0)\n    DPL: 0x0\nPresent: 0x0\n[2]: dt nt!_KGDTENTRY64 0xfffff8045215dfc0\n   Base: [0x0 -> 0x0]\n   Type: Code Execute/Read Accessed (0xb)\n    DPL: 0x0\nPresent: 0x1\n   Mode: 64b\n[3]: dt nt!_KGDTENTRY64 0xfffff8045215dfc8\n   Base: [0x0 -> 0x0]\n   Type: Data Read/Write Accessed (0x3)\n    DPL: 0x0\nPresent: 0x1\n[4]: dt nt!_KGDTENTRY64 0xfffff8045215dfd0\n   Base: [0x0 -> 0xffffffff]\n   Type: Code Execute/Read Accessed (0xb)\n    DPL: 0x3\nPresent: 0x1\n   Mode: 32b Compat\n[5]: dt nt!_KGDTENTRY64 0xfffff8045215dfd8\n   Base: [0x0 -> 0xffffffff]\n   Type: Data Read/Write Accessed (0x3)\n    DPL: 0x3\nPresent: 0x1\n[6]: dt nt!_KGDTENTRY64 0xfffff8045215dfe0\n   Base: [0x0 -> 0x0]\n   Type: Code Execute/Read Accessed (0xb)\n    DPL: 0x3\nPresent: 0x1\n   Mode: 64b\n[7]: dt nt!_KGDTENTRY64 0xfffff8045215dfe8\n   Base: [0x0 -> 0x0]\n   Type: Reserved (0x0)\n    DPL: 0x0\nPresent: 0x0\n[8]: dt nt!_KGDTENTRY64 0xfffff8045215dff0\n   Base: [0xfffff8045215c000 -> 0xfffff8045215c067]\n   Type: TSS64 Busy (0xb)\n    DPL: 0x0\nPresent: 0x1\n[9]: dt nt!_KGDTENTRY64 0xfffff8045215e000\n   Base: [0x326000 -> 0x329c00]\n   Type: Data Read/Write Accessed (0x3)\n    DPL: 0x3\nPresent: 0x1\n@$gdt()\n```"
  },
  {
    "path": "gdt/gdt.js",
    "content": "// Axel '0vercl0k' Souchet - October 8 2021\n\n'use strict';\n\n//\n// Small reminders from the manual intels on segments in x64:\n//   - Because ES, DS, and SS segment registers are not used in 64-bit mode, their fields (base, limit, and attribute) in\n//   segment descriptor registers are ignored.\n//   - Selector.Index selects one of 8192 descriptors in the GDT or LDT. The processor multiplies\n//   the index value by 8 (the number of bytes in a segment descriptor) and adds the result to the base\n//   address of the GDT or LDT (from the GDTR or LDTR register, respectively).\n//   - The first entry of the GDT is not used by the processor.\n//   - The hidden descriptor register fields for FS.base and GS.base are physically mapped to MSRs in order to load all\n//   address bits supported by a 64-bit implementation. Software with CPL = 0 (privileged software) can load all\n//   supported linear-address bits into FS.base or GS.base using WRMSR.\n//\n\nconst log = host.diagnostics.debugLog;\nconst logln = p => log(`${p}\\n`);\nconst hex = p => `0x${p.toString(16)}`;\n\nconst GdtSystemEntryTypes = new Map([\n    [0, 'Reserved'],\n    [1, 'Reserved'],\n    [2, 'LDT'],\n    [3, 'Reserved'],\n    [4, 'Reserved'],\n    [5, 'Reserved'],\n    [6, 'Reserved'],\n    [7, 'Reserved'],\n    [8, 'Reserved'],\n    [9, 'TSS64 Available'],\n    [10, 'Reserved'],\n    [11, 'TSS64 Busy'],\n    [12, 'CallGate64'],\n    [13, 'Reserved'],\n    [14, 'InterruptGate64'],\n    [15, 'TrapGate64'],\n]);\n\nconst GdtNonSystemEntryTypes = new Map([\n    [0, 'Data Read-Only'],\n    [1, 'Data Read-Only Accessed'],\n    [2, 'Data Read/Write'],\n    [3, 'Data Read/Write Accessed'],\n    [4, 'Data Read-Only Expand-Down'],\n    [5, 'Data Read-Only Expand-Down Accessed'],\n    [6, 'Data Read/Write Expand-Down'],\n    [7, 'Data Read/Write Expand-Down Accessed'],\n    [8, 'Code Execute-Only'],\n    [9, 'Code Execute-Only Accessed'],\n    [10, 'Code Execute/Read'],\n    [11, 'Code Execute/Read Accessed'],\n    [12, 'Code Execute-Only Conforming'],\n    [13, 'Code Execute-Only Conforming Accessed'],\n    [14, 'Code Execute/Read Conforming'],\n    [15, 'Code Execute/Read Conforming Accessed'],\n]);\n\nconst GdtEntryTypes = new Map([\n    [0, GdtSystemEntryTypes],\n    [1, GdtNonSystemEntryTypes]\n]);\n\nclass GdtEntry {\n    constructor(Addr) {\n        this._Addr = Addr;\n        const Entry = host.createPointerObject(Addr, 'nt', '_KGDTENTRY64*');\n        const LimitHigh = Entry.Bits.LimitHigh.bitwiseShiftLeft(16);\n        const LimitLow = Entry.LimitLow;\n        this._Limit = LimitHigh.add(LimitLow);\n        // For whatever reason _KGDTENTRY64 is 5 bits long. The intel manuals describes\n        // it as 4bits and the 'Descriptor type' bit.\n        // We grab the lower 4 bits as the type, and the MSB as the 'Descriptor type'.\n        this._Type = Entry.Bits.Type & 15;\n        this._NonSystem = (Entry.Bits.Type >> 4) & 1;\n        this._TypeS = GdtEntryTypes.get(this._NonSystem).get(this._Type);\n        // Note that system descriptors in IA-32e mode are 16 bytes instead\n        // of 8 bytes.\n        this._Size = 8;\n        if (!this._NonSystem && this._TypeS != 'Reserved') {\n            this._Size = 16;\n        }\n        this._Dpl = Entry.Bits.Dpl;\n        this._Granularity = Entry.Bits.Granularity;\n        this._Present = Entry.Bits.Present;\n        this._LongMode = Entry.Bits.LongMode;\n        this._DefaultBig = Entry.Bits.DefaultBig;\n        const BaseUpper = this._Size == 8 ? 0 : Entry.BaseUpper.bitwiseShiftLeft(32);\n        const BaseHigh = Entry.Bytes.BaseHigh.bitwiseShiftLeft(24);\n        const BaseMiddle = Entry.Bytes.BaseMiddle.bitwiseShiftLeft(16);\n        const BaseLow = Entry.BaseLow;\n        this._Base = BaseUpper.add(BaseHigh).add(BaseMiddle).add(BaseLow);\n        const Flags1 = Entry.Bytes.Flags1;\n        const Flags2 = Entry.Bytes.Flags2.bitwiseShiftLeft(8);\n        this._Attrs = Flags2.add(Flags1);\n    }\n\n    toString() {\n        const Increments = this._Granularity == 1 ? 1024 * 4 : 1;\n        // For example, when the granularity flag is set, a limit of 0 results in\n        // valid offsets from 0 to 4095.\n        const Size = this._Limit * Increments + (this._Granularity ? 0xfff : 0);\n        let S = `dt nt!_KGDTENTRY64 ${hex(this._Addr)}\n     Base: [${hex(this._Base)} -> ${hex(this._Base.add(Size))}]\n     Type: ${this._TypeS} (${hex(this._Type)})\n      DPL: ${hex(this._Dpl)}\n  Present: ${hex(this._Present)}\nAtributes: ${hex(this._Attrs)}`;\nif (this._TypeS.startsWith('Code')) {\n           S += `\n     Mode: ${this._LongMode ? '64b' : (this._DefaultBig ? '32b Compat' : '16b Compat')}`\n       }\n       return S;\n    }\n}\n\nfunction GetGdt() {\n    const Control = host.namespace.Debugger.Utility.Control;\n    const [_, GdtrValue] = Control.ExecuteCommand('r @gdtr').First().split('=');\n    const [__, GdtlValue] = Control.ExecuteCommand('r @gdtl').First().split('=');\n    return [host.parseInt64(GdtrValue, 16), host.parseInt64(GdtlValue, 16)];\n}\n\nfunction DumpGdtEntry(Addr) {\n    return new GdtEntry(Addr);\n}\n\nfunction DumpAllGdt() {\n    const [GdtBase, Gdtl] = GetGdt();\n    const GdtEnd = GdtBase.add(Gdtl);\n    logln(`Dumping the GDT from ${hex(GdtBase)} to ${hex(GdtEnd)}..`);\n    for (let CurrentEntry = GdtBase, Idx = 0;\n        CurrentEntry.compareTo(GdtEnd) < 0;\n        Idx++) {\n        const Entry = DumpGdtEntry(CurrentEntry);\n        logln(`[${Idx}]: ${Entry}`);\n        CurrentEntry = CurrentEntry.add(Entry._Size);\n    }\n}\n\nfunction DumpGdt(Selector) {\n    const [GdtBase, _] = GetGdt();\n    const Index = Selector.bitwiseShiftRight(3);\n    const Offset = Index.multiply(8);\n    const EntryAddress = GdtBase.add(Offset);\n    const Entry = DumpGdtEntry(EntryAddress);\n    logln(Entry);\n}\n\nfunction Gdt(Selector) {\n    const Attributes = host.currentSession.Attributes;\n    const IsKernel = Attributes.Target.IsKernelTarget;\n    //\n    // XXX: Not sure how to do this better?\n    // Attributes.Machine.PointerSize is 4 when running in a Wow64 thread :-/.\n    //\n    let Is64Bit = true;\n    try { host.createPointerObject(0, 'nt', '_KGDTENTRY64*'); } catch(e) { Is64Bit = false; }\n    if (!IsKernel || !Is64Bit) {\n        logln('The running session is not a kernel session or it is not running a 64-bit OS, so exiting');\n        return;\n    }\n\n    if (Selector == undefined) {\n        DumpAllGdt();\n    } else {\n        DumpGdt(Selector);\n    }\n}\n\nfunction initializeScript() {\n    return [\n        new host.apiVersionSupport(1, 3),\n        new host.functionAlias(\n            Gdt,\n            'gdt'\n        ),\n    ];\n}\n"
  },
  {
    "path": "parse_eh_win64/README.md",
    "content": "# parse_eh_win64.js\n\n`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.\n\nMore 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/).\n\n## Usage\n\nRun `.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.\n\n## Examples\n\n* At the process level, dumping `Function` objects and ordering them by the number of exception-handlers they define:\n\n```text\n0:002> dx @$curprocess.Functions.OrderByDescending(p => p.ExceptionHandlers.Count())\n@$curprocess.Functions.OrderByDescending(p => p.ExceptionHandlers.Count())                \n    [0x0]            : RVA:7fffb64bebf0 -> RVA:7fffb64bf022, 12 exception handlers\n    [0x1]            : RVA:7fffb8bdff80 -> RVA:7fffb8be0b67, 11 exception handlers\n    [0x2]            : RVA:7fffb1df8114 -> RVA:7fffb1df8360, 9 exception handlers\n    [0x3]            : RVA:7fffa0111354 -> RVA:7fffa01115a0, 9 exception handlers\n    [0x4]            : RVA:7fffb2183044 -> RVA:7fffb2183290, 9 exception handlers\n    [0x5]            : RVA:7fffa0d41344 -> RVA:7fffa0d41590, 9 exception handlers\n    [0x6]            : RVA:7fffb6573020 -> RVA:7fffb6573356, 6 exception handlers\n    [0x7]            : RVA:7fffb4c71f94 -> RVA:7fffb4c720b4, 6 exception handlers\n    [0x8]            : RVA:7fffb65e5774 -> RVA:7fffb65e5894, 6 exception handlers\n    [0x9]            : RVA:7fffb660c62c -> RVA:7fffb660cf2e, 6 exception handlers\n    [0xa]            : RVA:7fffb6c6f014 -> RVA:7fffb6c6f134, 6 exception handlers\n    [0xb]            : RVA:7fffb8b9a350 -> RVA:7fffb8b9b39b, 6 exception handlers\n    [0xc]            : RVA:7fffb35168a0 -> RVA:7fffb3516efb, 5 exception handlers\n```\n\n* Dumping a `Function` object:\n\n```text\n0:002> dx -r1 @$curprocess.Functions[0]\n@$curprocess.Functions[0]                 : RVA:7ff67025a6d0 -> RVA:7ff67025a738, 1 exception handlers\n    EHHandlerRVA     : 0x9b9700\n    EHHandler        : 0x7ff6708f9700\n    BeginRVA         : 0x31a6d0\n    EndRVA           : 0x31a738\n    Begin            : 0x7ff67025a6d0\n    End              : 0x7ff67025a738\n    ExceptionHandlers :   __try {7ff67025a6fb -> 7ff67025a712} __except(EXCEPTION_EXECUTE_HANDLER) {7ff67025a736}\n```\n\n* At the module level, dumping `ExceptionHandler` objects:\n\n```text\n0:002> dx @$curprocess.Modules[0].ExceptionHandlers\n@$curprocess.Modules[0].ExceptionHandlers                 : Exception handlers\n    [0x0]            :   __try {7ff67025a6fb -> 7ff67025a712} __except(EXCEPTION_EXECUTE_HANDLER) {7ff67025a736}\n    [0x1]            :   __try {7ff6708f80b3 -> 7ff6708f813e} __except(7ff6708f93f2()) {7ff6708f813e}\n    [0x2]            :   __try {7ff6708f90fd -> 7ff6708f9202} __except(7ff6708f9425()) {7ff6708f9202}\n    [0x3]            :   __try {7ff6708f9236 -> 7ff\n```\n\n* Dumping an `ExceptionHandler` object:\n\n```text\n0:002> dx @$curprocess.Modules[0].ExceptionHandlers[0]\n@$curprocess.Modules[0].ExceptionHandlers[0]                 :   __try {7ff67025a6fb -> 7ff67025a712} __except(EXCEPTION_EXECUTE_HANDLER) {7ff67025a736}\n    Begin            : 0x7ff67025a6fb\n    End              : 0x7ff67025a712\n    HandlerAddress   : 0x1\n    JumpTarget       : 0x7ff67025a736\n    IsTryFinally     : false\n    HasFilter        : false\n```\n\n* Dumping the current call-stack with EH information:\n\n```text\n0:002> !ehhandlers\n5 stack frames, scanning for handlers...\nFrame 1: EHHandler: 7fffb8c1fc90: ntdll!_C_specific_handler:\n              Except: 7fffb8c5ef1d: ntdll!DbgUiRemoteBreakin+0x4d:\nFrame 3: EHHandler: 7fffb8c1fc90: ntdll!_C_specific_handler:\n              Except: 7fffb8bfa267: ntdll!RtlUserThreadStart+0x37:\n              Filter: 7fffb8c38021: ntdll!RtlUserThreadStart$filt$0:\n@$ehhandlers()  \n```\n\n"
  },
  {
    "path": "parse_eh_win64/parse_eh_win64.js",
    "content": "﻿// Axel '0vercl0k' Souchet - Dec 2017\n\n\"use strict\";\n\nlet log = host.diagnostics.debugLog;\nlet logln = function (e) {\n    host.diagnostics.debugLog(e + '\\n');\n};\n\nfunction read_u32(addr) {\n    return host.memory.readMemoryValues(addr, 1, 4)[0];\n}\n\nfunction read_u8(addr) {\n    return host.memory.readMemoryValues(addr, 1, 1)[0];\n}\n\nclass ScopeRecord {\n    // 0:000> dt SCOPE_RECORD\n    //   +0x000 BeginAddress     : Uint4B\n    //   +0x004 EndAddress       : Uint4B\n    //   +0x008 HandlerAddress   : Uint4B\n    //   +0x00c JumpTarget       : Uint4B\n    constructor(ScopeRecordAddress) {\n        this.Begin = read_u32(ScopeRecordAddress);\n        this.End = read_u32(ScopeRecordAddress.add(0x4));\n        this.HandlerAddress = read_u32(ScopeRecordAddress.add(0x8));\n        this.JumpTarget = read_u32(ScopeRecordAddress.add(0xc));\n    }\n\n    get IsTryFinally() {\n        return this.JumpTarget.compareTo(0) == 0;\n    }\n\n    get HasFilter() {\n        return this.IsTryFinally == false &&\n            this.HandlerAddress.compareTo(1) != 0;\n    }\n\n    InBound(Begin, End) {\n        return this.Begin.compareTo(Begin) >= 0 &&\n            this.End.compareTo(End) < 0;\n    }\n\n    toString() {\n        let S = '  __try {'\n        S += this.Begin.toString(16) + ' -> ' + this.End.toString(16);\n        S += '}';\n        if (this.IsTryFinally == true) {\n            S += ' __finally {';\n            S += this.HandlerAddress.toString(16);\n        } else {\n            S += ' __except(';\n            if (this.HasFilter == false) {\n                S += 'EXCEPTION_EXECUTE_HANDLER';\n            } else {\n                S += this.HandlerAddress.toString(16) + '()';\n            }\n            S += ') {';\n            S += this.JumpTarget.toString(16);\n        }\n        S += '}';\n        return S;\n    }\n}\n\nclass Function {\n    constructor(BaseAddress, EHHandler, Begin, End) {\n        this.__BaseAddress = BaseAddress;\n        this.EHHandlerRVA = EHHandler;\n        this.EHHandler = BaseAddress.add(EHHandler);\n        this.BeginRVA = Begin;\n        this.EndRVA = End;\n        this.Begin = BaseAddress.add(Begin);\n        this.End = BaseAddress.add(End);\n        this.ExceptionHandlers = [];\n    }\n\n    SetRecords(Records, KeepRVA = false) {\n        this.ExceptionHandlers = Records;\n        if (KeepRVA) {\n            return;\n        }\n\n        for (let ExceptionHandler of this.ExceptionHandlers) {\n            ExceptionHandler.Begin = ExceptionHandler.Begin.add(this.__BaseAddress);\n            ExceptionHandler.End = ExceptionHandler.End.add(this.__BaseAddress);\n            if (ExceptionHandler.JumpTarget.compareTo(0) != 0) {\n                ExceptionHandler.JumpTarget = ExceptionHandler.JumpTarget.add(this.__BaseAddress);\n            }\n            if (ExceptionHandler.HandlerAddress.compareTo(1) != 0) {\n                ExceptionHandler.HandlerAddress = ExceptionHandler.HandlerAddress.add(this.__BaseAddress);\n            }\n        }\n    }\n\n    toString() {\n        let S = 'RVA:' + this.Begin.toString(16) + ' -> RVA:' + this.End.toString(16);\n        S += ', ' + this.ExceptionHandlers.length + ' exception handlers';\n        return S;\n    }\n}\n\nfunction ParseCSpecificHandlerDatas(\n    ScopeCount,\n    ScopeRecords,\n    Function\n) {\n    // 0:000> ?? sizeof(SCOPE_RECORD)\n    // unsigned int64 0x10\n    let Records = [];\n    let ScopeSize = ScopeCount.multiply(0x10);\n    for (let i = 0; i < ScopeSize; i += 0x10) {\n        let CurrentScope = ScopeRecords.add(i);\n        let Record = new ScopeRecord(CurrentScope);\n        if (Record.InBound(Function.BeginRVA, Function.EndRVA) == false) {\n            return [];\n        }\n        Records.push(Record);\n    }\n    return Records;\n}\n\nfunction ExtractExceptionHandlersForModule(\n    BaseAddress,\n    KeepRVA = false\n) {\n    let EHANDLER = 1;\n    let IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3;\n\n    // 0:000> dt _IMAGE_DOS_HEADER e_lfanew\n    //   +0x03c e_lfanew : Int4B\n    let NtHeaders = BaseAddress.add(read_u32(BaseAddress.add(0x3c)));\n\n    // 0:000> dt _IMAGE_NT_HEADERS64 OptionalHeader\n    //   +0x018 OptionalHeader : _IMAGE_OPTIONAL_HEADER64\n    // 0:000> dt _IMAGE_OPTIONAL_HEADER64 DataDirectory\n    //   +0x070 DataDirectory : [16] _IMAGE_DATA_DIRECTORY\n    // 0:000> dt _IMAGE_DATA_DIRECTORY\n    //   +0x000 VirtualAddress   : Uint4B\n    //   +0x004 Size             : Uint4B\n    let EntryExceptionDirectory = NtHeaders.add(0x18 + 0x70 + (IMAGE_DIRECTORY_ENTRY_EXCEPTION * 8));\n    let RuntimeFunctionEntry = BaseAddress.add(read_u32(EntryExceptionDirectory));\n    let SizeOfDirectory = read_u32(EntryExceptionDirectory.add(4));\n    let Functions = [];\n\n    for (let i = 0; i < SizeOfDirectory; i += 0xC) {\n        // 0:000> dt _IMAGE_RUNTIME_FUNCTION_ENTRY\n        //   +0x000 BeginAddress     : Uint4B\n        //   +0x004 EndAddress       : Uint4B\n        //   +0x008 UnwindInfoAddress : Uint4B\n        //   +0x008 UnwindData       : Uint4B\n        // 0:000> ?? sizeof(_IMAGE_RUNTIME_FUNCTION_ENTRY)\n        // unsigned int64 0xc\n        let CurrentEntry = RuntimeFunctionEntry.add(i);\n        let BeginAddress = read_u32(CurrentEntry);\n        let EndAddress = read_u32(CurrentEntry.add(4));\n\n        if (BeginAddress.compareTo(0) == 0 || EndAddress.compareTo(0) == 0) {\n            continue;\n        }\n\n        // 0:000> dt UNWIND_INFO\n        //   +0x000 Version          : Pos 0, 3 Bits\n        //   +0x000 Flags            : Pos 3, 5 Bits\n        //   +0x001 SizeOfProlog     : UChar\n        //   +0x002 CountOfCodes     : UChar\n        //   +0x003 FrameRegister    : Pos 0, 4 Bits\n        //   +0x003 FrameOffset      : Pos 4, 4 Bits\n        //   +0x004 UnwindCode       : [1] UNWIND_CODE\n        let UnwindInfo = BaseAddress.add(read_u32(CurrentEntry.add(8)));\n        let UnwindInfoFlags = read_u8(UnwindInfo).bitwiseShiftRight(3);\n        if (UnwindInfoFlags.bitwiseAnd(EHANDLER).compareTo(0) == 0) {\n            continue;\n        }\n\n        // 0:000> ?? sizeof(UNWIND_CODE)\n        // unsigned int64 2\n        let CountOfCodes = read_u8(UnwindInfo.add(2));\n        // For alignment purposes, this array will always have an even number of entries,\n        // with the final entry potentially unused (in which case the array will be one\n        // longer than indicated by the count of unwind codes field).\n        let AlignedCountOfCodes = (CountOfCodes + 1) & ~1;\n\n        // 0:000> dt UNWIND_INFO_END\n        //   +0x000 ExceptionHandler : Uint4B\n        //   +0x004 ExceptionData    : Uint4B\n        let UnwindInfoEnd = UnwindInfo.add(4 + (AlignedCountOfCodes * 2));\n        let ExceptionHandler = read_u32(UnwindInfoEnd);\n\n        // 0:000> dt SEH_SCOPE_TABLE\n        //   +0x000 Count            : Uint4B\n        //   +0x004 ScopeRecord      : [1] SCOPE_RECORD\n        let ScopeTable = UnwindInfoEnd.add(4);\n        let ScopeCount = read_u32(ScopeTable);\n        if (ScopeCount == 0) {\n            continue;\n        }\n\n        let Records = ScopeTable.add(4);\n        let CurrentFunction = new Function(\n            BaseAddress, ExceptionHandler,\n            BeginAddress, EndAddress\n        );\n        Records = ParseCSpecificHandlerDatas(ScopeCount, Records, CurrentFunction);\n        if (Records.length == 0) {\n            continue;\n        }\n\n        CurrentFunction.SetRecords(Records, KeepRVA);\n        Functions.push(CurrentFunction);\n    }\n\n    return Functions;\n}\n\nclass ModelExceptionHandlers {\n    constructor(Type, Inst) {\n        this.__module = null;\n        this.__process = null;\n        if (Type == 'Module') {\n            this.__module = Inst;\n        } else {\n            this.__process = Inst;\n        }\n        this.__handlers = null;\n        // We do not parse the exception handlers information here\n        // as this constructor is called everytime you do:\n        //  dx @$curprocess\n        // so we will only parse the information when the user asked for it.\n    }\n\n    __ExceptionHandlers(Modules) {\n        let Handlers = [];\n        for (let Module of Modules) {\n            let Functions = ExtractExceptionHandlersForModule(Module.BaseAddress);\n            for (let Function of Functions) {\n                Handlers = Handlers.concat(Function.ExceptionHandlers);\n            }\n        }\n        return Handlers;\n    }\n\n    *[Symbol.iterator]() {\n        if (this.__handlers == null) {\n            // Only parse the infornmation once everytine we query it.\n            let Modules = [this.__module];\n            if (this.__process != null) {\n                Modules = this.__process.Modules;\n            }\n            this.__handlers = this.__ExceptionHandlers(Modules);\n        }\n\n        for (let Handler of this.__handlers) {\n            yield Handler;\n        }\n    }\n\n    toString() {\n        return 'Exception handlers';\n    }\n}\n\nclass ModelFunctions {\n    constructor(Type, Inst) {\n        this.__module = null;\n        this.__process = null;\n        if (Type == 'Module') {\n            this.__module = Inst;\n        } else {\n            this.__process = Inst;\n        }\n        this.__functions = null;\n    }\n\n    __Functions(Modules) {\n        let Functions = [];\n        for (let Module of Modules) {\n            let CurrentFunctions = ExtractExceptionHandlersForModule(Module.BaseAddress);\n            Functions = Functions.concat(CurrentFunctions);\n        }\n        return Functions;\n    }\n\n    *[Symbol.iterator]() {\n        if (this.__functions == null) {\n            this.__functions = [];\n            let Modules = [this.__module];\n            if (this.__process != null) {\n                Modules = this.__process.Modules;\n            }\n\n            this.__functions = this.__Functions(Modules);\n        }\n\n        for (let Function of this.__functions) {\n            yield Function;\n        }\n    }\n\n    toString() {\n        return 'Functions';\n    }\n}\n\nclass __ProcessModelExtension {\n    get ExceptionHandlers() {\n        return new ModelExceptionHandlers('Process', this);\n    }\n\n    get Functions() {\n        return new ModelFunctions('Process', this);\n    }\n}\n\nclass __ModuleModelExtension {\n    get ExceptionHandlers() {\n        return new ModelExceptionHandlers('Module', this);\n    }\n\n    get Functions() {\n        return new ModelFunctions('Module', this);\n    }\n}\n\nfunction BangEHHandlers() {\n    let Control = host.namespace.Debugger.Utility.Control;\n    let CurrentThread = host.currentThread;\n    let CurrentProcess = host.currentProcess;\n    let Registers = CurrentThread.Registers.User;\n\n    let ReturnAddresses = [Registers.rip];\n    let Frames = CurrentThread.Stack.Frames;\n    for (let Frame of Frames) {\n        ReturnAddresses.push(Frame.Attributes.ReturnOffset);\n    }\n\n    logln(ReturnAddresses.length + ' stack frames, scanning for handlers...');\n    let Functions = Array.from(CurrentProcess.Functions);\n    for (let Entry of ReturnAddresses.entries()) {\n        let FrameNumber = host.Int64(Entry[0]);\n        let ReturnAddress = Entry[1];\n        let Func = Functions.find(\n            c => ReturnAddress.compareTo(c.Begin) >= 0 &&\n                ReturnAddress.compareTo(c.End) < 0\n        );\n\n        if (Func == undefined) {\n            continue;\n        }\n\n        let ExceptionHandlers = Array.from(Func.ExceptionHandlers);\n        let ExceptionHandler = ExceptionHandlers.find(\n            c => ReturnAddress.compareTo(c.Begin) >= 0 &&\n                ReturnAddress.compareTo(c.End) < 0\n        )\n\n        if (ExceptionHandler == undefined) {\n            continue;\n        }\n\n        let Filter = undefined;\n        let EHHandler = Func.EHHandler;\n        let Handler = ExceptionHandler.HandlerAddress;\n        let Name = 'Finally';\n        if (ExceptionHandler.IsTryFinally == false) {\n            if (ExceptionHandler.HasFilter) {\n                Filter = ExceptionHandler.HandlerAddress;\n            }\n            Handler = ExceptionHandler.JumpTarget;\n            Name = ' Except';\n        }\n\n        let FormatAddress = function (Handler) {\n            let S = Handler.toString(16) + ': ';\n            S += Control.ExecuteCommand(\n                'u ' + Handler.toString(16) + ' l1'\n            ).First();\n            return S;\n        }\n\n        logln('Frame ' + FrameNumber.toString(16) + ': EHHandler: ' + FormatAddress(EHHandler));\n        logln('             ' + Name + ': ' + FormatAddress(Handler));\n        if (Filter != undefined) {\n            logln('              Filter: ' + FormatAddress(Filter));\n        }\n    }\n}\n\nfunction initializeScript() {\n    return [\n        new host.namedModelParent(\n            __ProcessModelExtension,\n            'Debugger.Models.Process'\n        ),\n        new host.namedModelParent(\n            __ModuleModelExtension,\n            'Debugger.Models.Module'\n        ),\n        new host.functionAlias(\n            BangEHHandlers,\n            'ehhandlers'\n        )\n    ];\n}\n"
  },
  {
    "path": "policybuffer/README.md",
    "content": "# policybuffer.js\n\n`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.\n\n## Usage\n\nRun `.scriptload policybuffer.js` to load the script. You can invoke the disassembler feature with `!disasspolicy <addr>`.\n\n## Examples\n\n* Dumping the File System policy of Firefox:\n\n```text\n0:017> !disasspolicy 0x00000131`d2142208\n!OP_NUMBER_AND_MATCH<Param1, 0x1>\n!OP_WSTRING_MATCH<Param0, \"\\??\\\", Length(0x4), Offset(0x0), CASE_SENSITIVE>\nOP_ACTION<ASK_BROKER>\n\n!OP_NUMBER_AND_MATCH<Param1, 0x1>\nOP_WSTRING_MATCH<Param0, \"~\", Length(0x1), Offset(0xffffffff), CASE_SENSITIVE>\nOP_ACTION<ASK_BROKER>\n\nOP_WSTRING_MATCH<Param0, \"\\??\\C:\\Users\\over\\AppData\\LocalLow\\Mozilla\\Temp-{8aca3358-7266-42d0-a521-805394768d86}\\\", Length(0x57), Offset(0x0), CASE_INSENSITIVE>\nOP_ACTION<ASK_BROKER>\n\n!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>\nOP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>\nOP_WSTRING_MATCH<Param0, \"\\??\\C:\\Users\\over\\AppData\\Local\\Microsoft\\Windows\\Fonts\\\", Length(0x38), Offset(0x0), CASE_INSENSITIVE>\nOP_ACTION<ASK_BROKER>\n\n!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>\nOP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>\nOP_WSTRING_MATCH<Param0, \"\\??\\C:\\work\\firefox-66.0a1.en-US.win64\\firefox\\\", Length(0x2f), Offset(0x0), CASE_INSENSITIVE>\nOP_ACTION<ASK_BROKER>\n\n!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>\nOP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>\nOP_WSTRING_MATCH<Param0, \"\\??\\C:\\Users\\over\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\rbo6kdsb.default-nightly\\chrome\\\", Length(0x5b), Offset(0x0), CASE_INSENSITIVE>\nOP_ACTION<ASK_BROKER>\n\n!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>\nOP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>\nOP_WSTRING_MATCH<Param0, \"\\??\\C:\\Users\\over\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\rbo6kdsb.default-nightly\\extensions\\\", Length(0x5f), Offset(0x0), CASE_INSENSITIVE>\nOP_ACTION<ASK_BROKER>\n\n!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>\nOP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>\nOP_WSTRING_MATCH<Param0, \"\\??\\C:\\Users\\over\\AppData\\Roaming\\Mozilla\\SystemExtensionsDev\\\", Length(0x3e), Offset(0x0), CASE_INSENSITIVE>\nOP_ACTION<ASK_BROKER>\n\n!OP_NUMBER_AND_MATCH<Param2, 0x5fedff56>\nOP_NUMBER_MATCH<Param3, UINT32_TYPE(0x1)>\nOP_WSTRING_MATCH<Param0, \"\\??\\C:\\Users\\over\\AppData\\Roaming\\Mozilla\\Extensions\\\", Length(0x35), Offset(0x0), CASE_INSENSITIVE>\nOP_ACTION<ASK_BROKER>\n\nOP_WSTRING_MATCH<Param0, \"\\??\\pipe\\chrome.\", Length(0x10), Offset(0x0), CASE_INSENSITIVE>\nOP_ACTION<ASK_BROKER>\n\nOP_WSTRING_MATCH<Param0, \"\\??\\pipe\\gecko-crash-server-pipe.\", Length(0x21), Offset(0x0), CASE_INSENSITIVE>\nOP_ACTION<ASK_BROKER>\n\n@$disasspolicy(0x00000131`d2142208)\n```\n"
  },
  {
    "path": "policybuffer/policybuffer.js",
    "content": "﻿// Axel '0vercl0k' Souchet - 5 March 2019\n// sandbox::PolicyBase::EvalPolicy\n\n'use strict';\n\n//\n// Utility functions.\n//\n\nconst Log = host.diagnostics.debugLog;\nconst Logln = p => host.diagnostics.debugLog(p + '\\n');\nconst Hex = p => '0x' + p.toString(16);\nconst ReadWstring = p => host.memory.readWideString(p);\n\nfunction ReadShort(Address) {\n    let Value = null;\n    try {\n        Value = host.memory.readMemoryValues(\n           Address, 1, 2\n        )[0];\n    } catch(e) {\n    }\n\n    return Value;\n}\n\nfunction ReadDword(Address) {\n    let Value = null;\n    try {\n        Value = host.memory.readMemoryValues(\n           Address, 1, 4\n        )[0];\n    } catch(e) {\n    }\n\n    return Value;\n}\n\nfunction ReadQword(Address) {\n    let Value = null;\n    try {\n        Value = host.memory.readMemoryValues(\n           Address, 1, 8\n        )[0];\n    } catch(e) {\n    }\n\n    return Value;\n}\n\nfunction initializeScript() {\n    return [\n        new host.apiVersionSupport(1, 3)\n    ];\n}\n\n//\n// Constants.\n//\n\n// The low-level policy is implemented using the concept of policy 'opcodes'.\n// An opcode is a structure that contains enough information to perform one\n// comparison against one single input parameter. For example, an opcode can\n// encode just one of the following comparison:\n//\n// - Is input parameter 3 not equal to NULL?\n// - Does input parameter 2 start with L\"c:\\\\\"?\n// - Is input parameter 5, bit 3 is equal 1?\n//\n// Each opcode is in fact equivalent to a function invocation where all\n// the parameters are known by the opcode except one. So say you have a\n// function of this form:\n//      bool fn(a, b, c, d)  with 4 arguments\n//\n// Then an opcode is:\n//      op(fn, b, c, d)\n// Which stores the function to call and its 3 last arguments\n//\n// Then and opcode evaluation is:\n//      op.eval(a)  ------------------------> fn(a,b,c,d)\n//                        internally calls\n//\n// The idea is that complex policy rules can be split into streams of\n// opcodes which are evaluated in sequence. The evaluation is done in\n// groups of opcodes that have N comparison opcodes plus 1 action opcode:\n//\n// [comparison 1][comparison 2]...[comparison N][action][comparison 1]...\n//    ----- evaluation order----------->\n//\n// Each opcode group encodes one high-level policy rule. The rule applies\n// only if all the conditions on the group evaluate to true. The action\n// opcode contains the policy outcome for that particular rule.\n\n// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h#77\n// The following are the implemented opcodes.\n// enum OpcodeID {\n// OP_ALWAYS_FALSE,  // Evaluates to false (EVAL_FALSE).\n// OP_ALWAYS_TRUE,  // Evaluates to true (EVAL_TRUE).\n// OP_NUMBER_MATCH,  // Match a 32-bit integer as n == a.\n// OP_NUMBER_MATCH_RANGE,  // Match a 32-bit integer as a <= n <= b.\n// OP_NUMBER_AND_MATCH,  // Match using bitwise AND; as in: n & a != 0.\n// OP_WSTRING_MATCH,  // Match a string for equality.\n// OP_ACTION  // Evaluates to an action opcode.\n// };\n\nconst OP_ALWAYS_FALSE = 0;\nconst OP_ALWAYS_TRUE = 1;\nconst OP_NUMBER_MATCH = 2;\nconst OP_NUMBER_MATCH_RANGE = 3;\nconst OP_NUMBER_AND_MATCH = 4;\nconst OP_WSTRING_MATCH = 5;\nconst OP_ACTION = 6;\n\nconst Opcodes = {\n    [OP_ALWAYS_FALSE] : 'OP_ALWAYS_FALSE',\n    [OP_ALWAYS_TRUE] : 'OP_ALWAYS_TRUE',\n    [OP_NUMBER_MATCH] : 'OP_NUMBER_MATCH',\n    [OP_NUMBER_MATCH_RANGE] : 'OP_NUMBER_MATCH_RANGE',\n    [OP_NUMBER_AND_MATCH] : 'OP_NUMBER_AND_MATCH',\n    [OP_WSTRING_MATCH] : 'OP_WSTRING_MATCH',\n    [OP_ACTION] : 'OP_ACTION'\n};\n\n// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h\n// enum StringMatchOptions {\n//  CASE_SENSITIVE = 0,      // Pay or Not attention to the case as defined by\n//  CASE_INSENSITIVE = 1,    // RtlCompareUnicodeString windows API.\n//  EXACT_LENGHT = 2         // Don't do substring match. Do full string match.\n// };\n\nconst MatchingOptions = {\n    0 : 'CASE_SENSITIVE',\n    1 : 'CASE_INSENSITIVE',\n    2 : 'EXACT_LENGTH',\n    3 : 'EXACT_LENGTH | CASE_INSENSITIVE'\n};\n\n// These are the possible policy outcomes. Note that some of them might\n// not apply and can be removed. Also note that The following values only\n// specify what to do, not how to do it and it is acceptable given specific\n// cases to ignore the policy outcome.\n// enum EvalResult {\n//   // Comparison opcode values:\n//   EVAL_TRUE,   // Opcode condition evaluated true.\n//   EVAL_FALSE,  // Opcode condition evaluated false.\n//   EVAL_ERROR,  // Opcode condition generated an error while evaluating.\n//   // Action opcode values:\n//   ASK_BROKER,  // The target must generate an IPC to the broker. On the broker\n//                // side, this means grant access to the resource.\n//   DENY_ACCESS,   // No access granted to the resource.\n//   GIVE_READONLY,  // Give readonly access to the resource.\n//   GIVE_ALLACCESS,  // Give full access to the resource.\n//   GIVE_CACHED,  // IPC is not required. Target can return a cached handle.\n//   GIVE_FIRST,  // TODO(cpu)\n//   SIGNAL_ALARM,  // Unusual activity. Generate an alarm.\n//   FAKE_SUCCESS,  // Do not call original function. Just return 'success'.\n//   FAKE_ACCESS_DENIED,  // Do not call original function. Just return 'denied'\n//                        // and do not do IPC.\n//   TERMINATE_PROCESS,  // Destroy target process. Do IPC as well.\n// };\n\nconst Actions = {\n    3 : 'ASK_BROKER',\n    4 : 'DENY_ACCESS',\n    5 : 'GIVE_READONLY',\n    6 : 'GIVE_ALLACCESS',\n    7 : 'GIVE_CACHED',\n    8 : 'GIVE_FIRST',\n    9 : 'SIGNAL_ALARM',\n    10 : 'FAKE_SUCCESS',\n    11 : 'FACE_ACCESS_DENIED',\n    12 : 'TERMINATE_PROCESS'\n};\n\n// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/internal_types.h#19\n// enum ArgType {\n//   INVALID_TYPE = 0,\n//   WCHAR_TYPE,\n//   UINT32_TYPE,\n//   UNISTR_TYPE,\n//   VOIDPTR_TYPE,\n//   INPTR_TYPE,\n//   INOUTPTR_TYPE,\n//   LAST_TYPE\n// };\n\nconst ArgTypes = {\n    0 : 'INVALID_TYPE',\n    1 : 'WCHAR_TYPE',\n    2 : 'UINT32_TYPE',\n    3 : 'UNISTR_TYPE',\n    4 : 'VOIDPTR_TYPE',\n    5 : 'INPTR_TYPE',\n    6 : 'INOUTPTR_TYPE',\n    7 : 'LAST_TYPE'\n};\n\n// Options that apply to every opcode. They are specified when creating\n// each opcode using OpcodeFactory::MakeOpXXXXX() family of functions\n// Do nothing special.\nconst kPolNone = 0;\n\n// Convert EVAL_TRUE into EVAL_FALSE and vice-versa. This allows to express\n// negated conditions such as if ( a && !b).\nconst kPolNegateEval = 1;\n\n// Zero the MatchContext context structure. This happens after the opcode\n// is evaluated.\nconst kPolClearContext = 2;\n\n// Use OR when evaluating this set of opcodes. The policy evaluator by default\n// uses AND when evaluating. Very helpful when\n// used with kPolNegateEval. For example if you have a condition best expressed\n// as if(! (a && b && c)), the use of this flags allows it to be expressed as\n// if ((!a) || (!b) || (!c)).\nconst kPolUseOREval = 4;\n\n// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/policy_params.h#36\nconst ParameterNames = {\n    'OpenFile' : [\n        'NAME', 'BROKER', 'ACCESS', 'DISPOSITION', 'OPTIONS'\n    ],\n};\n\n//\n// Code.\n//\n\nfunction DisassPolicyBuffer(PolicyBufferAddress, PolicyType) {\n    let Ptr = PolicyBufferAddress;\n    const PolicyBufferOpcodeCount = ReadQword(Ptr);\n    Ptr += 8;\n    for(let Idx = 0; Idx < PolicyBufferOpcodeCount; ++Idx) {\n\n        //\n        // Save off the current pointer as it is useful to compute\n        // where the stored string is in memory for the OP_WSTRING_MATCH\n        // opcode.\n        //\n\n        const OpcodePtr = Ptr;\n\n        //\n        // Unpack the opcode structure.\n        //\n\n        const OpcodeId = ReadDword(Ptr);\n        Ptr += 4;\n        const SelectedParameter = ReadShort(Ptr);\n        Ptr += 2;\n        const Options = ReadShort(Ptr);\n        Ptr += 2;\n        const Parameters = [];\n        for(let InnerIdx = 0; InnerIdx < 4; ++InnerIdx) {\n            Parameters.push(ReadQword(Ptr));\n            Ptr += 8;\n        }\n\n        //\n        // Once we dumped the opcode, let's prettify its parameters.\n        //\n\n        const Operands = [];\n        let FirstOperand = 'Param' + SelectedParameter;\n        if(ParameterNames[PolicyType] != undefined) {\n            FirstOperand = PolicyType + '::' + ParameterNames[PolicyType][SelectedParameter];\n        }\n\n        Operands.push(FirstOperand);\n        if(OpcodeId == OP_ALWAYS_TRUE || OpcodeId == OP_ALWAYS_FALSE) {\n        } else if(OpcodeId == OP_NUMBER_MATCH) {\n            const ArgType = ArgTypes[Parameters[1].asNumber()];\n            Operands.push(ArgType + '(' + Hex(Parameters[0]) + ')');\n        } else if(OpcodeId == OP_NUMBER_MATCH_RANGE) {\n            Operands.push('LowerBound(' + Hex(Parameters[0]) + ')');\n            Operands.push('UpperBound(' + Hex(Parameters[1]) + ')');\n        } else if(OpcodeId == OP_NUMBER_AND_MATCH) {\n            Operands.push(Hex(Parameters[0]));\n        }else if(OpcodeId == OP_WSTRING_MATCH) {\n            const Displacement = Parameters[0];\n            const StringAddress = OpcodePtr.add(Displacement);\n            Operands.push('\"' + ReadWstring(StringAddress) + '\"');\n            Operands.push('Length(' + Hex(Parameters[1]) + ')');\n            Operands.push('Offset(' + Hex(Parameters[2]) + ')');\n            const MatchingOption = Parameters[3].asNumber();\n            Operands.push(MatchingOptions[MatchingOption]);\n        } else if(OpcodeId == OP_ACTION) {\n\n            //\n            // The OP_ACTION is the only opcode that does not need a selected\n            // parameter.\n            //\n\n            const Action = Actions[Parameters[0].asNumber()];\n            Operands[0] = Action;\n        }\n\n        //\n        // Display the opcode and its operands.\n        //\n\n        const OpcodeIdStr = Opcodes[OpcodeId];\n        if(Options.bitwiseAnd(kPolNegateEval).compareTo(0) != 0) {\n            Logln('!' + OpcodeIdStr + '<' + Operands.join(', ')  + '>');\n        } else {\n            Logln(OpcodeIdStr + '<' + Operands.join(', ')  + '>');\n        }\n\n        if(OpcodeId == OP_ACTION) {\n            Logln('');\n        }\n    }\n}\n\nfunction initializeScript() {\n    return [\n        new host.apiVersionSupport(1, 3),\n        new host.functionAlias(\n            DisassPolicyBuffer,\n            'disasspolicy'\n        )\n    ];\n}\n\n"
  },
  {
    "path": "sm/README.md",
    "content": "# sm.js\n\n`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.\n\nThe 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.\n\nIt 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).\n\n## Usage\n\nRun `.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.\n\n## Examples\n\n* 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]`:\n\n```text\n0:000> !smdump_jsvalue vp[2].asBits_\n1e5f10024c0: js!js::ArrayObject:   Length: 10\n1e5f10024c0: js!js::ArrayObject: Capacity: 10\n1e5f10024c0: js!js::ArrayObject:  Content: ['short', 13.37, new Map(...), ['loooooooooooooooooooooooooooooong', [0x1337, {'doare' : 'in d4 place'}]], false, null, undefined, true, atan2(), Math]\n@$smdump_jsvalue(vp[2].asBits_)\n```\n\n* Setting a breakpoint in code being JIT'd by IonMonkey:\n\n```text\n0:008> g\nBreakpoint 0 hit\njs!js::jit::CodeGenerator::visitBoundsCheck:\n00007ff7`87d9e1a0 4156            push    r14\n\n0:000> !ion_insertbp\nunsigned char 0xcc ''\nunsigned int64 0x5b\n@$ion_insertbp()\n\n0:000> g\n(1a60.f58): Break instruction exception - code 80000003 (first chance)\n000003d9`ca67991b cc              int     3\n\n0:000> u . l2\n000003d9`ca67991b cc              int     3\n000003d9`ca67991c 3bd8            cmp     ebx,eax\n```\n\n* Figure out if an object lives in the Nursery heap:\n\n```text\n0:008> !in_nursery 0x19767e00df8\nUsing previously cached JSContext @0x000001fe17318000\n0x000001fe1731cde8: js::Nursery\n ChunkCountLimit: 0x0000000000000010 (16 MB)\n        Capacity: 0x0000000000fffe80 bytes\n    CurrentChunk: 0x0000019767e00000\n        Position: 0x0000019767e00eb0\n          Chunks:\n            00: [0x0000019767e00000 - 0x0000019767efffff]\n            01: [0x00001fa2aee00000 - 0x00001fa2aeefffff]\n            02: [0x0000115905000000 - 0x00001159050fffff]\n            03: [0x00002fc505200000 - 0x00002fc5052fffff]\n            04: [0x000020d078700000 - 0x000020d0787fffff]\n            05: [0x0000238217200000 - 0x00002382172fffff]\n            06: [0x00003ff041f00000 - 0x00003ff041ffffff]\n            07: [0x00001a5458700000 - 0x00001a54587fffff]\n-------\n0x19767e00df8 has been found in the js::NurseryChunk @0x19767e00000!\n\n0:008> !in_nursery 0x00001fe174be810\nUsing previously cached JSContext @0x000001fe17318000\n0x000001fe1731cde8: js::Nursery\n ChunkCountLimit: 0x0000000000000010 (16 MB)\n        Capacity: 0x0000000000fffe80 bytes\n    CurrentChunk: 0x0000019767e00000\n        Position: 0x0000019767e00eb0\n          Chunks:\n            00: [0x0000019767e00000 - 0x0000019767efffff]\n            01: [0x00001fa2aee00000 - 0x00001fa2aeefffff]\n            02: [0x0000115905000000 - 0x00001159050fffff]\n            03: [0x00002fc505200000 - 0x00002fc5052fffff]\n            04: [0x000020d078700000 - 0x000020d0787fffff]\n            05: [0x0000238217200000 - 0x00002382172fffff]\n            06: [0x00003ff041f00000 - 0x00003ff041ffffff]\n            07: [0x00001a5458700000 - 0x00001a54587fffff]\n-------\n0x1fe174be810 hasn't been found be in any Nursery js::NurseryChunk.\n```\n"
  },
  {
    "path": "sm/sm.js",
    "content": "﻿// Axel '0vercl0k' Souchet - 24-June-2018\n//\n// Example:\n//   * from the interpreter:\n//       Math.atan2(['short', 13.37, new Map([[ 1, 'one' ],[ 2, 'two' ]]), ['loooooooooooooooooooooooooooooong', [0x1337, {doare:'in d4 place'}]], false, null, undefined, true, Math.atan2, Math])\n//\n//   * from the debugger:\n//       js!js::math_atan2:\n//       00007ff6`0227e140 56              push    rsi\n//       0:000> !smdump_jsvalue vp[2].asBits_\n//       1e5f10024c0: js!js::ArrayObject:   Length: 10\n//       1e5f10024c0: js!js::ArrayObject: Capacity: 10\n//       1e5f10024c0: js!js::ArrayObject:  Content: ['short', 13.37, new Map(...), ['loooooooooooooooooooooooooooooong', [0x1337, {'doare' : 'in d4 place'}]], false, null, undefined, true, atan2(), Math]\n//       @$smdump_jsvalue(vp[2].asBits_)\n//\n\n'use strict';\n\nlet Module = null;\n\nconst logln = p => host.diagnostics.debugLog(p + '\\n');\nconst hex = p => '0x' + p.toString(16).padStart(16, '0');\nconst JSVAL_TAG_SHIFT = host.Int64(47);\nconst JSVAL_PAYLOAD_MASK = host.Int64(1).bitwiseShiftLeft(JSVAL_TAG_SHIFT).subtract(1);\nconst CLASS_NON_NATIVE = host.Int64(0x40000);\nconst FLAG_DELEGATE = host.Int64(8);\n\nconst JSVAL_TYPE_DOUBLE = host.Int64(0x1fff0);\nconst JSVAL_TYPE_INT32 = host.Int64(0x1fff1);\nconst JSVAL_TYPE_BOOLEAN = host.Int64(0x1fff2);\nconst JSVAL_TYPE_UNDEFINED = host.Int64(0x1fff3);\nconst JSVAL_TYPE_NULL = host.Int64(0x1fff4);\nconst JSVAL_TYPE_MAGIC = host.Int64(0x1fff5);\nconst JSVAL_TYPE_STRING = host.Int64(0x1fff6);\nconst JSVAL_TYPE_SYMBOL = host.Int64(0x1fff7);\nconst JSVAL_TYPE_OBJECT = host.Int64(0x1fffc);\n\nconst INLINE_CHARS_BIT = host.Int64(1 << 6);\nconst LATIN1_CHARS_BIT = host.Int64(1 << 9);\n\nconst JSID_TYPE_MASK = host.Int64(0x7);\nconst JSID_TYPE_STRING = host.Int64(0x0);\nconst JSID_TYPE_INT = host.Int64(0x1);\nconst JSID_TYPE_VOID = host.Int64(0x2);\nconst JSID_TYPE_SYMBOL = host.Int64(0x4);\n\nconst SLOT_MASK = host.Int64(0xffffff);\nconst FIXED_SLOTS_SHIFT = host.Int64(24);\n\nconst FunctionConstants = {\n    0x0001 : 'INTERPRETED',\n    0x0004 : 'EXTENDED',\n    0x0008 : 'BOUND_FUN',\n    0x0010 : 'WASM_OPTIMIZED',\n    0x0020 : 'HAS_GUESSED_ATOM/HAS_BOUND_FUNCTION_NAME_PREFIX',\n    0x0040 : 'LAMBDA',\n    0x0080 : 'SELF_HOSTED',\n    0x0100 : 'HAS_INFERRED_NAME',\n    0x0200 : 'INTERPRETED_LAZY',\n    0x0400 : 'RESOLVED_LENGTH',\n    0x0800 : 'RESOLVED_NAME',\n};\n\nconst FunctionKindConstants = {\n    0 : 'NORMAL_KIND',\n    1 : 'ARROW_KIND',\n    2 : 'METHOD_KIND',\n    3 : 'CLASSCONSTRUCTOR_KIND',\n    4 : 'GETTER_KIND',\n    5 : 'SETTER_KIND',\n    6 : 'ASMJS_KIND'\n};\n\nconst Tag2Names = {\n    [JSVAL_TYPE_DOUBLE] : 'Double',\n    [JSVAL_TYPE_INT32] : 'Int32',\n    [JSVAL_TYPE_STRING] : 'String',\n    [JSVAL_TYPE_UNDEFINED] : 'Undefined',\n    [JSVAL_TYPE_BOOLEAN] : 'Boolean',\n    [JSVAL_TYPE_NULL] : 'Null',\n    [JSVAL_TYPE_OBJECT] : 'Object',\n    [JSVAL_TYPE_SYMBOL] : 'Symbol',\n    [JSVAL_TYPE_MAGIC] : 'Magic',\n};\n\n//\n// Read a uint64_t integer from Addr.\n//\n\nfunction read_u64(Addr) {\n    return host.memory.readMemoryValues(Addr, 1, 8)[0];\n}\n\n//\n// Mirror the functionality of ::fromElements.\n//\n\nfunction heapslot_to_objectelements(Addr) {\n    // static ObjectElements* fromElements(HeapSlot* elems) {\n    //  return reinterpret_cast<ObjectElements*>(uintptr_t(elems) - sizeof(ObjectElements));\n    // }\n    const ObjectElementsSize = host.getModuleType(Module, 'js::ObjectElements').size;\n    const ObjectElements = host.createPointerObject(\n        Addr.subtract(ObjectElementsSize),\n        Module,\n        'js::ObjectElements*'\n    );\n\n    return ObjectElements;\n}\n\n//\n// Is Byte printable?\n//\n\nfunction printable(Byte) {\n    return Byte >= 0x20 && Byte <= 0x7e;\n}\n\n//\n// Return a string describing Byte; either a \\x41 or its ascii representation\n//\n\nfunction byte_to_str(Byte) {\n    if(printable(Byte)) {\n        return String.fromCharCode(Byte);\n    }\n\n    return '\\\\x' + Byte.toString(16).padStart(2, '0');\n}\n\n//\n// Is this jsid an integer?\n//\n\nfunction jsid_is_int(Propid) {\n    const Bits = Propid.value.asBits;\n    return Bits.bitwiseAnd(JSID_TYPE_MASK).compareTo(JSID_TYPE_INT) == 0;\n}\n\n//\n// Is this jsid a string?\n//\n\nfunction jsid_is_string(Propid) {\n    const Bits = Propid.value.asBits;\n    return Bits.bitwiseAnd(JSID_TYPE_MASK).compareTo(JSID_TYPE_STRING) == 0;\n}\n\n//\n// Retrieve a property from a Shape; returns an actual integer/string based\n// on the propid_.\n//\n\nfunction get_property_from_shape(Shape) {\n    // XXX: expose a smdump_jsid\n    const Propid = Shape.propid_;\n    if(jsid_is_int(Propid)) {\n        return Propid.value.asBits.bitwiseShiftRight(1);\n    }\n\n    if(jsid_is_string(Propid)) {\n        return new __JSString(Propid.value.asBits);\n    }\n\n    // XXX: todo\n}\n\nfunction jsvalue_to_instance(Addr) {\n    const JSValue = new __JSValue(Addr);\n    if(!Tag2Names.hasOwnProperty(JSValue.Tag)) {\n        return 'Dunno';\n    }\n\n    const Name = Tag2Names[JSValue.Tag];\n    const Type = Names2Types[Name];\n    return new Type(JSValue.Payload);\n}\n\nclass __JSMagic {\n    constructor(Addr) {\n        this._Addr = Addr;\n    }\n\n    toString() {\n        return 'magic';\n    }\n\n    Logger(Content) {\n        logln(this._Addr.toString(16) + ': JSVAL_TYPE_MAGIC: ' + Content);\n    }\n\n    Display() {\n        this.Logger(this);\n    }\n}\n\nclass __JSArgument {\n    //  * ArgumentsObject instances use the following reserved slots:\n    //  *\n    //  *   INITIAL_LENGTH_SLOT\n    //  *     Stores the initial value of arguments.length, plus a bit indicating\n    //  *     whether arguments.length and/or arguments[@@iterator] have been\n    //  *     modified.  Use initialLength(), hasOverriddenLength(), and\n    //  *     hasOverriddenIterator() to access these values.  If arguments.length has\n    //  *     been modified, then the current value of arguments.length is stored in\n    //  *     another slot associated with a new property.\n    //  *   DATA_SLOT\n    //  *     Stores an ArgumentsData*, described above.\n    //  *   MAYBE_CALL_SLOT\n    //  *     Stores the CallObject, if the callee has aliased bindings. See\n    //  *     the ArgumentsData::args comment.\n    //  *   CALLEE_SLOT\n    //  *     Stores the initial arguments.callee. This value can be overridden on\n    //  *     mapped arguments objects, see hasOverriddenCallee.\n    //  */\n    // class ArgumentsObject : public NativeObject {\n    //     protected:\n    //      static const uint32_t INITIAL_LENGTH_SLOT = 0;\n    //      static const uint32_t DATA_SLOT = 1;\n    //      static const uint32_t MAYBE_CALL_SLOT = 2;\n    //      static const uint32_t CALLEE_SLOT = 3;\n    constructor(Addr) {\n        this._Addr = Addr;\n        this._Obj = host.createPointerObject(\n            Addr,\n            Module,\n            'js::ArgumentsObject*'\n        );\n\n        const INITIAL_LENGTH_SLOT = host.Int64(0);\n        const DATA_SLOT = host.Int64(1);\n        const MAYBE_CALL_SLOT = host.Int64(2);\n        const CALLEE_SLOT = host.Int64(3);\n        const ArgumentsObjectSize = this._Obj.dereference().targetType.size;\n        this._SlotAddress = this._Obj.address.add(ArgumentsObjectSize);\n\n        const InitialLengthSlot = read_u64(this._SlotAddress.add(\n            INITIAL_LENGTH_SLOT.multiply(8)\n        ));\n        this._InitialLength = new __JSInt32(InitialLengthSlot)._Value;\n\n        this._Data = read_u64(this._SlotAddress.add(\n            DATA_SLOT.multiply(8)\n        )).bitwiseShiftLeft(1);\n    }\n\n    toString() {\n        return 'Arguments(..)';\n    }\n\n    Logger(Content) {\n        logln(this._Addr.toString(16) + ': js!js::ArgumentsObject: ' + Content);\n    }\n\n    Display() {\n        this.Logger('InitialLength: ' + this._InitialLength);\n        this.Logger('         Data: ' + hex(this._Data) + ' (js!js::ArgumentsData)');\n    }\n}\n\nclass __JSNull {\n    constructor(Addr) {\n        this._Addr = Addr;\n    }\n\n    toString() {\n        return 'null';\n    }\n\n    Logger(Content) {\n        logln(this._Addr.toString(16) + ': JSVAL_TYPE_NULL: ' + Content);\n    }\n\n    Display() {\n        this.Logger(this);\n    }\n}\n\nclass __JSUndefined {\n    constructor(Addr) {\n        this._Addr = Addr;\n    }\n\n    toString() {\n        return 'undefined';\n    }\n\n    Logger(Content) {\n        logln(this.Addr.toString(16) + ': JSVAL_TYPE_UNDEFINED: ' + Content);\n    }\n\n    Display() {\n        this.Logger(this);\n    }\n}\n\nclass __JSBoolean {\n    constructor(Addr) {\n        this._Addr = Addr;\n        this._Value = Addr.compareTo(1) == 0 ? true : false;\n    }\n\n    toString() {\n        return this._Value.toString();\n    }\n\n    Logger(Content) {\n        logln(this._Addr.toString(16) + ': JSVAL_TYPE_BOOLEAN: ' + Content);\n    }\n\n    Display() {\n        this.Logger(this);\n    }\n}\n\nclass __JSInt32 {\n    constructor(Addr) {\n        this._Addr = Addr;\n        this._Value = Addr.bitwiseAnd(0xffffffff);\n    }\n\n    toString() {\n        return '0x' + this._Value.toString(16);\n    }\n\n    Logger(Content) {\n        logln(this._Addr.toString(16) + ': JSVAL_TYPE_INT32: ' + Content);\n    }\n\n    Display() {\n        this.Logger(this);\n    }\n}\n\nclass __JSString {\n    constructor(Addr) {\n        this._Obj = host.createPointerObject(\n            Addr,\n            Module,\n            'JSString*'\n        );\n        /*\n         * The Flags Word\n         *\n         * The flags word stores both the string's type and its character encoding.\n         *\n         * If LATIN1_CHARS_BIT is set, the string's characters are stored as Latin1\n         * instead of TwoByte. This flag can also be set for ropes, if both the\n         * left and right nodes are Latin1. Flattening will result in a Latin1\n         * string in this case.\n         */\n        const Flags = this._Obj.d.flags_;\n        const IsLatin1 = Flags.bitwiseAnd(LATIN1_CHARS_BIT).compareTo(0) != 0;\n        const IsInline = Flags.bitwiseAnd(INLINE_CHARS_BIT).compareTo(0) != 0;\n        let Address = null;\n        if(IsInline) {\n\n            //\n            // inlineStorageLatin1 and inlineStorageTwoByte are in a union and\n            // as a result are at the same address\n            //\n\n            Address = this._Obj.d.inlineStorageLatin1.address;\n        } else {\n\n            //\n            // Same as above with nonInlineStorageLatin1 and nonInlineStorageTwoByte.\n            //\n\n            Address = this._Obj.d.s.u2.nonInlineCharsLatin1.address;\n        }\n\n        let Length = Flags.bitwiseShiftRight(32);\n        if(!IsLatin1) {\n            Length *= 2;\n        }\n\n        this._String = Array.from(host.memory.readMemoryValues(\n            Address,\n            Length,\n            1\n        )).map(\n            p => byte_to_str(p)\n        ).join('');\n    }\n\n    toString() {\n        return \"'\" + this._String + \"'\";\n    }\n\n    Logger(Content) {\n        logln(this._Obj.address.toString(16) + ': js!JSString: ' + Content);\n    }\n\n    Display() {\n        this.Logger(this);\n    }\n}\n\nclass __JSValue {\n    constructor(Addr) {\n        this._Addr = Addr;\n        this._Tag = this._Addr.bitwiseShiftRight(JSVAL_TAG_SHIFT);\n        this._IsDouble = this._Tag.compareTo(JSVAL_TYPE_DOUBLE) < 0;\n        this._Payload = this._Addr.bitwiseAnd(JSVAL_PAYLOAD_MASK);\n    }\n\n    get Payload() {\n        if(this._IsDouble) {\n            return this._Addr;\n        }\n\n        return this._Payload;\n    }\n\n    get Tag() {\n        if(this._IsDouble) {\n            return JSVAL_TYPE_DOUBLE;\n        }\n\n        return this._Tag;\n    }\n}\n\nclass __JSArray {\n    constructor(Addr) {\n        this._Obj = host.createPointerObject(\n            Addr,\n            Module,\n            'js::ArrayObject*'\n        );\n        // XXX: why doesn't it work?\n        // this.Obj.elements_.value.address\n        this._Content = this._Obj.elements_.address;\n        this._Header = heapslot_to_objectelements(this._Content);\n        // The flags word stores both the flags and the number of shifted elements.\n        // Allow shifting 2047 elements before actually moving the elements.\n        const NumShiftedElementsBits = host.Int64(11);\n        const NumShiftedElementsShift = host.Int64(32).subtract(NumShiftedElementsBits);\n        this._Flags = this._Header.flags;\n        this._NumShifted = this._Flags.bitwiseShiftRight(NumShiftedElementsShift)\n        this._Length = this._Header.length;\n        this._Capacity = this._Header.capacity;\n        this._InitializedLength = this._Header.initializedLength;\n    }\n\n    toString() {\n        const Max = 10;\n        const Content = [];\n        for(let Idx = 0; Idx < Math.min(Max, this._InitializedLength); ++Idx) {\n            const Addr = this._Content.add(Idx * 8);\n            const JSValue = read_u64(Addr);\n            const Inst = jsvalue_to_instance(JSValue);\n            Content.push(Inst.toString());\n        }\n\n        return '[' + Content.join(', ') + (this._Length > Max ? ', ...' : '') + ']';\n    }\n\n    Logger(Content) {\n        logln(this._Obj.address.toString(16) + ': js!js::ArrayObject: ' + Content);\n    }\n\n    Display() {\n        this.Logger('           Length: ' + this._Length);\n        this.Logger('         Capacity: ' + this._Capacity);\n        this.Logger('InitializedLength: ' + this._InitializedLength);\n        this.Logger('       NumShifted: ' + this._NumShifted + ' (flags: ' + hex(this._Flags) + ')');\n        this.Logger('          Content: ' + this);\n    }\n}\n\nclass __JSFunction {\n    constructor(Addr) {\n        this._Obj = host.createPointerObject(\n            Addr,\n            Module,\n            'JSFunction*'\n        );\n\n        this._Atom = this._Obj.atom_.value.address;\n        this._Name = '<anonymous>';\n        if(this._Atom.compareTo(0) != 0) {\n            this._Name = new __JSString(this._Atom).toString().slice(1, -1);\n        }\n\n        this._Name += '()';\n        this._Flags = this._Obj.flags_;\n    }\n\n    toString() {\n        return this._Name;\n    }\n\n    get Flags() {\n        const S = [];\n        for(const Key in FunctionConstants) {\n            if(this._Flags.bitwiseAnd(host.parseInt64(Key)).compareTo(0) != 0) {\n                S.push(FunctionConstants[Key]);\n            }\n        }\n\n        const Kind = (this._Flags >> 13) & 7;\n        S.push(FunctionKindConstants[Kind]);\n        return S.join(' | ');\n    }\n\n    Logger(Content) {\n        logln(this._Obj.address.toString(16) + ': js!JSFunction: ' + Content);\n    }\n\n    Display() {\n        this.Logger(this);\n        this.Logger('Flags: ' + this.Flags);\n    }\n}\n\nclass __JSSymbol {\n    constructor(Addr) {\n        this._Obj = host.createPointerObject(\n            Addr,\n            Module,\n            'js::Symbol*'\n        );\n    }\n\n    toString() {\n        const Desc = new __JSString(this._Obj.description_.address);\n        return 'Symbol(' + Desc + ')';\n    }\n\n    Logger(Content) {\n        logln(this.Obj_.address.toString(16) + ': js!js::Symbol: ' + Content);\n    }\n\n    Display() {\n        this.Logger(this);\n    }\n}\n\nclass __JSArrayBuffer {\n    constructor(Addr) {\n        this._Obj = host.createPointerObject(\n            Addr,\n            Module,\n            'js::ArrayBufferObject*'\n        );\n\n        const ArrayBufferObjectSize = host.getModuleType(Module, 'js::ArrayBufferObject').size;\n        // static const uint8_t DATA_SLOT = 0;\n        // static const uint8_t BYTE_LENGTH_SLOT = 1;\n        const ByteLengthSlotAddr = Addr.add(ArrayBufferObjectSize).add(1 * 8);\n        const ByteLengthSlot = read_u64(ByteLengthSlotAddr);\n        this._ByteLength = new __JSInt32(ByteLengthSlot)._Value;\n        // static const uint8_t FIRST_VIEW_SLOT = 2;\n        // static const uint8_t FLAGS_SLOT = 3;\n        const FlagsAddr = Addr.add(ArrayBufferObjectSize).add(3 * 8);\n        const FlagsSlot = read_u64(FlagsAddr);\n        this._Flags = new __JSInt32(FlagsSlot)._Value;\n    }\n\n    get Flags() {\n        //  enum BufferKind {\n        //      PLAIN               = 0, // malloced or inline data\n        //      WASM                = 1,\n        //      MAPPED              = 2,\n        //      EXTERNAL            = 3,\n        //      KIND_MASK           = 0x3\n        //  };\n        // enum ArrayBufferFlags {\n        //     // The flags also store the BufferKind\n        //     BUFFER_KIND_MASK    = BufferKind::KIND_MASK,\n        //     DETACHED            = 0x4,\n        //     // The dataPointer() is owned by this buffer and should be released\n        //     // when no longer in use. Releasing the pointer may be done by freeing,\n        //     // invoking a dereference callback function, or unmapping, as\n        //     // determined by the buffer's other flags.\n        //     //\n        //     // Array buffers which do not own their data include buffers that\n        //     // allocate their data inline, and buffers that are created lazily for\n        //     // typed objects with inline storage, in which case the buffer points\n        //     // directly to the typed object's storage.\n        //     OWNS_DATA           = 0x8,\n        //     // This array buffer was created lazily for a typed object with inline\n        //     // data. This implies both that the typed object owns the buffer's data\n        //     // and that the list of views sharing this buffer's data might be\n        //     // incomplete. Any missing views will be typed objects.\n        //     FOR_INLINE_TYPED_OBJECT = 0x10,\n        //     // Views of this buffer might include typed objects.\n        //     TYPED_OBJECT_VIEWS  = 0x20,\n        //     // This PLAIN or WASM buffer has been prepared for asm.js and cannot\n        //     // henceforth be transferred/detached.\n        //     FOR_ASMJS           = 0x40\n        // };\n        const BufferKinds = {\n            0 : 'PLAIN',\n            1 : 'WASM',\n            2 : 'MAPPED',\n            3 : 'EXTERNAL'\n        };\n\n        const BufferKind = BufferKinds[this._Flags.bitwiseAnd(3).asNumber()];\n        const ArrayBufferFlags = [\n            'BufferKind(' + BufferKind + ')'\n        ];\n\n        const ArrayBufferFlagsConstants = {\n            [0x04] : 'DETACHED',\n            [0x08] : 'OWNS_DATA',\n            [0x10] : 'FOR_INLINE_TYPED_OBJECT',\n            [0x20] : 'TYPED_OBJECT_VIEWS',\n            [0x40] : 'FOR_ASMJS'\n        };\n\n        for(const Key in ArrayBufferFlagsConstants) {\n            if(this._Flags.bitwiseAnd(host.parseInt64(Key)).compareTo(0) != 0) {\n                ArrayBufferFlags.push(ArrayBufferFlagsConstants[Key]);\n            }\n        }\n\n        return ArrayBufferFlags.join(' | ');\n    }\n\n    get ByteLength() {\n        return this._ByteLength;\n    }\n\n    toString() {\n        return 'ArrayBuffer({ByteLength:' + this._ByteLength + ', ...})';\n    }\n\n    Logger(Content) {\n        logln(this._Obj.address.toString(16) + ': js!js::ArrayBufferObject: ' + Content);\n    }\n\n    Display() {\n        this.Logger('ByteLength: ' + this.ByteLength);\n        this.Logger('     Flags: ' + this.Flags);\n        this.Logger('   Content: ' + this);\n    }\n}\n\nclass __JSTypedArray {\n    constructor(Addr) {\n        this._Obj = host.createPointerObject(\n            Addr,\n            Module,\n            'js::TypedArrayObject*'\n        );\n\n        const Group = this._Obj.group_.value;\n        this._TypeName = host.memory.readString(Group.clasp_.name)\n        const Sizes = {\n            'Float64Array' : 8,\n            'Float32Array' : 4,\n            'Uint32Array' : 4,\n            'Int32Aray' : 4,\n            'Uint16Array' : 2,\n            'Int16Array' : 2,\n            'Uint8Array' : 1,\n            'Uint8ClampedArray' : 1,\n            'Int8Array' : 1\n        };\n        this._ElementSize = Sizes[this._TypeName];\n\n        const TypedArrayObjectSize = host.getModuleType(Module, 'js::TypedArrayObject').size;\n        // static const size_t BUFFER_SLOT = 0;\n        // static const size_t LENGTH_SLOT = 1;\n        const LengthSlotAddr = Addr.add(TypedArrayObjectSize).add(1 * 8);\n        const LengthSlot = read_u64(LengthSlotAddr);\n        this._Length = new __JSInt32(LengthSlot)._Value;\n        this._ByteLength = this._Length * this._ElementSize;\n        // static const size_t BYTEOFFSET_SLOT = 2;\n        const ByteOffsetSlotAddr = Addr.add(TypedArrayObjectSize).add(2 * 8);\n        const ByteOffsetSlot = read_u64(ByteOffsetSlotAddr);\n        this._ByteOffset = new __JSInt32(ByteOffsetSlot)._Value;\n        // static const size_t RESERVED_SLOTS = 3;\n    }\n\n    get Type() {\n        return this._TypeName;\n    }\n\n    get ByteOffset() {\n        return this._ByteOffset;\n    }\n\n    get ByteLength() {\n        return this._ByteLength;\n    }\n\n    get Length() {\n        return this._Length;\n    }\n\n    toString() {\n        return this._TypeName + '({Length:' + this._Length + ', ...})';\n    }\n\n    Logger(Content) {\n        logln(this._Obj.address.toString(16) + ': js!js::TypedArrayObject: ' + Content);\n    }\n\n    Display() {\n        this.Logger('      Type: ' + this.Type);\n        this.Logger('    Length: ' + this.Length);\n        this.Logger('ByteLength: ' + this.ByteLength);\n        this.Logger('ByteOffset: ' + this.ByteOffset);\n        this.Logger('   Content: ' + this);\n    }\n}\n\nclass __JSMap {\n    constructor(Addr) {\n        this._Addr = Addr;\n    }\n\n    // XXX: TODO\n    toString() {\n        return 'new Map(...)';\n    }\n\n    Logger(Content) {\n        logln(this._Addr.toString(16) + ': js!js::MapObject: ' + Content);\n    }\n\n    Display() {\n        this.Logger('Content: ' + this);\n    }\n}\n\nclass __JSDouble {\n    constructor(Addr) {\n        this._Addr = Addr;\n    }\n\n    toString() {\n        const U32 = new Uint32Array([\n            this._Addr.getLowPart(),\n            this._Addr.getHighPart()\n        ]);\n        const F64 = new Float64Array(U32.buffer);\n        return F64[0];\n    }\n\n    Logger(Content) {\n        logln(this._Addr.toString(16) + ': JSVAL_TYPE_DOUBLE: ' + Content);\n    }\n\n    Display() {\n        this.Logger(this);\n    }\n}\n\nconst Names2Types = {\n    'Function' : __JSFunction,\n    'Array' : __JSArray,\n    'ArrayBuffer' : __JSArrayBuffer,\n    'Map' : __JSMap,\n    'Int32' : __JSInt32,\n    'String' : __JSString,\n    'Boolean' : __JSBoolean,\n    'Null' : __JSNull,\n    'Undefined' : __JSUndefined,\n    'Symbol' : __JSSymbol,\n    'Double' : __JSDouble,\n    'Magic' : __JSMagic,\n    'Arguments' : __JSArgument,\n\n    'Float64Array' : __JSTypedArray,\n    'Float32Array' : __JSTypedArray,\n    'Uint32Array' : __JSTypedArray,\n    'Int32Array' : __JSTypedArray,\n    'Uint16Array' : __JSTypedArray,\n    'Int16Array' : __JSTypedArray,\n    'Uint8Array' : __JSTypedArray,\n    'Uint8ClampedArray' : __JSTypedArray,\n    'Int8Array' : __JSTypedArray\n};\n\nclass __JSObject {\n    /* JSObject.h\n     * A JavaScript object.\n     *\n     * This is the base class for all objects exposed to JS script (as well as some\n     * objects that are only accessed indirectly). Subclasses add additional fields\n     * and execution semantics. The runtime class of an arbitrary JSObject is\n     * identified by JSObject::getClass().\n     *\n     * The members common to all objects are as follows:\n     *\n     * - The |group_| member stores the group of the object, which contains its\n     *   prototype object, its class and the possible types of its properties.\n     *\n     * - The |shapeOrExpando_| member points to (an optional) guard object that JIT\n     *   may use to optimize. The pointed-to object dictates the constraints\n     *   imposed on the JSObject:\n     *      nullptr\n     *          - Safe value if this field is not needed.\n     *      js::Shape\n     *          - All objects that might point |shapeOrExpando_| to a js::Shape\n     *            must follow the rules specified on js::ShapedObject.\n     *      JSObject\n     *          - Implies nothing about the current object or target object. Either\n     *            of which may mutate in place. Store a JSObject* only to save\n     *            space, not to guard on.\n     */\n    constructor(Addr) {\n        this._Addr = Addr;\n        this._Obj = host.createPointerObject(\n            this._Addr,\n            Module,\n            'JSObject*'\n        );\n\n        this._Properties = [];\n        const Group = this._Obj.group_.value;\n        this._ClassName = host.memory.readString(Group.clasp_.name);\n        const NonNative = Group.clasp_.flags.bitwiseAnd(CLASS_NON_NATIVE).compareTo(0) != 0;\n        if(NonNative) {\n            return;\n        }\n\n        const Shape = host.createPointerObject(\n            this._Obj.shapeOrExpando_.address,\n            Module,\n            'js::Shape*'\n        );\n\n        const NativeObject = host.createPointerObject(Addr, Module, 'js::NativeObject*');\n\n        if(this._ClassName == 'Array') {\n\n            //\n            // Optimization for 'length' property if 'Array' cf\n            // js::ArrayObject::length / js::GetLengthProperty\n            //\n\n            const ObjectElements = heapslot_to_objectelements(NativeObject.elements_.address);\n            this._Properties.push('length : ' + ObjectElements.length);\n            return;\n        }\n\n        //\n        // Walk the list of Shapes and get the property names\n        //\n\n        const Properties = {};\n        let CurrentShape = Shape;\n        while(CurrentShape.parent.value.address.compareTo(0) != 0) {\n            const SlotIdx = CurrentShape.immutableFlags.bitwiseAnd(SLOT_MASK).asNumber();\n            Properties[SlotIdx] = get_property_from_shape(CurrentShape);\n            CurrentShape = CurrentShape.parent.value;\n        }\n\n        //\n        // Walk the slots to get the values now (check NativeGetPropertyInline/GetExistingProperty)\n        //\n\n        const NativeObjectTypeSize = host.getModuleType(Module, 'js::NativeObject').size;\n        const NativeObjectElements = NativeObject.address.add(NativeObjectTypeSize);\n        const NativeObjectSlots = NativeObject.slots_.address;\n        const Max = Shape.immutableFlags.bitwiseShiftRight(FIXED_SLOTS_SHIFT).asNumber();\n        for(let Idx = 0; Idx < Object.keys(Properties).length; Idx++) {\n\n            //\n            // Check out NativeObject::getSlot()\n            //\n\n            const PropertyName = Properties[Idx];\n            let PropertyValue = undefined;\n            let ElementAddr = undefined;\n            if(Idx < Max) {\n                ElementAddr = NativeObjectElements.add(Idx * 8);\n            } else {\n                ElementAddr = NativeObjectSlots.add((Idx - Max) * 8);\n            }\n\n            const JSValue = read_u64(ElementAddr);\n            PropertyValue = jsvalue_to_instance(JSValue);\n            this._Properties.push(PropertyName + ' : ' + PropertyValue);\n        }\n    }\n\n    get Properties() {\n        return this._Properties;\n    }\n\n    get ClassName() {\n        return this._ClassName;\n    }\n\n    toString() {\n        if(this._ClassName != 'Object' && Names2Types.hasOwnProperty(this._ClassName)) {\n            const Type = Names2Types[this._ClassName];\n            return new Type(this._Addr).toString();\n        }\n\n        if(this._ClassName != 'Object') {\n            return this._ClassName;\n        }\n\n        if(this._Properties != undefined && this._Properties.length > 0) {\n            return '{' + this._Properties.join(', ') + '}';\n        }\n\n        if(this._ClassName == 'Object') {\n            return '[Object]';\n        }\n\n        return 'Dunno';\n    }\n\n    Logger(Content) {\n        logln(this._Addr.toString(16) + ': js!JSObject: ' + Content);\n    }\n\n    Display() {\n        this.Logger('Content: ' + this);\n\n        //\n        // If the class name is not Object then it means the toString() method\n        // might already have displayed the properties.\n        // {foo:'bar'} VS Math.\n        //\n\n        if(this._ClassName != 'Object') {\n            this.Logger('Properties: {' + this._Properties.join(', ') + '}');\n        }\n    }\n}\n\nNames2Types['Object'] = __JSObject;\n\nfunction smdump_jsobject(Addr, Type = null) {\n    init();\n\n    if(Addr.hasOwnProperty('address')) {\n        Addr = Addr.address;\n    }\n\n    let ClassName;\n    if(Type == 'Object' || Type == null) {\n        const JSObject = new __JSObject(Addr);\n        ClassName = JSObject.ClassName;\n        if(!Names2Types.hasOwnProperty(ClassName)) {\n            JSObject.Display();\n        }\n    } else {\n        ClassName = Type;\n    }\n\n    if(Names2Types.hasOwnProperty(ClassName)) {\n        const Inst = new Names2Types[ClassName](Addr);\n        Inst.Display();\n    }\n}\n\nfunction smdump_jsvalue(Addr) {\n    init();\n\n    if(Addr == undefined) {\n        logln('!smdump_jsvalue <jsvalue object addr>');\n        return;\n    }\n\n    //\n    // Ensure Addr is an unsigned value. If we don't do this\n    // the shift operations don't behave the way we want them to.\n    //\n\n    Addr = Addr.bitwiseAnd(host.parseInt64('0xffffffffffffffff'));\n    const JSValue = new __JSValue(Addr);\n    if(!Tag2Names.hasOwnProperty(JSValue.Tag)) {\n        logln('Tag ' +  JSValue.Tag.toString(16) + ' not recognized');\n        return;\n    }\n\n    const Name = Tag2Names[JSValue.Tag];\n    return smdump_jsobject(JSValue.Payload, Name);\n}\n\nfunction init() {\n    if(Module != null) {\n        return;\n    }\n\n    const Xul = host.currentProcess.Modules.Any(\n        p => p.Name.toLowerCase().endsWith('xul.dll')\n    );\n\n    if(Xul) {\n        Module = 'xul.dll';\n        logln('Detected xul.dll, using it as js module.');\n        return;\n    }\n\n    Module = 'js.exe';\n}\n\nfunction ion_insertbp() {\n    // XXX: Having the current frame would be better.. but not sure if\n    // this is something possible?\n    const CurrentThread = host.currentThread;\n    const LowestFrame = CurrentThread.Stack.Frames[0];\n    const LocalVariables = LowestFrame.LocalVariables;\n    const CodeGenerator = LocalVariables.this;\n    if(CodeGenerator === undefined ||\n       CodeGenerator.targetType.toString() != 'js::jit::CodeGenerator *') {\n        logln('The script expects `this` to be a js::jit::CodeGenerator in the lowest frame.');\n        return;\n    }\n\n    const JITBuffer = CodeGenerator.masm.masm.m_formatter.m_buffer.m_buffer;\n    // XXX: So sounds like I can't do JITBuffer.mBegin[JITBuffer.mLength] = 0xcc,\n    // so here I am writing ugly things :x\n    const BreakpointAddress = JITBuffer.mBegin.address.add(JITBuffer.mLength);\n    logln(`JIT buffer is at ${hex(JITBuffer.mBegin.address)}`);\n    logln(`Writing breakpoint at ${hex(BreakpointAddress)}`);\n    host.evaluateExpression(`*(char*)${hex(BreakpointAddress)} = 0xcc`);\n    JITBuffer.mLength += 1;\n}\n\nlet Context = undefined;\nfunction dump_nursery_stats(Nursery) {\n    logln(`${hex(Nursery.address)}: js::Nursery`);\n    const ChunkCountLimit = Nursery.chunkCountLimit_;\n    const NurseryChunk = host.createPointerObject(\n        0,\n        Module,\n        'js::NurseryChunk*'\n    );\n    const ChunkSize = NurseryChunk.dereference().targetType.size;\n    const MaxSize = ChunkCountLimit.multiply(ChunkSize);\n    logln(` ChunkCountLimit: ${hex(ChunkCountLimit)} (${MaxSize / 1024 / 1024} MB)`);\n    const Capacity = Nursery.capacity_;\n    logln(`        Capacity: ${hex(Capacity)} bytes`)\n    const CurrentChunk = Nursery.currentStartPosition_;\n    logln(`    CurrentChunk: ${hex(CurrentChunk)}`);\n    const Position = Nursery.position_;\n    logln(`        Position: ${hex(Position)}`);\n    logln('          Chunks:');\n    const Chunks = Nursery.chunks_;\n    for(let Idx = 0; Idx < Chunks.mLength; Idx++) {\n        const Chunk = Chunks.mBegin[Idx];\n        const StartAddress = Chunk.data.address;\n        const EndAddress = StartAddress.add(ChunkSize).subtract(1);\n        const PaddedIdx = Idx.toString().padStart(2, '0');\n        logln(`            ${PaddedIdx}: [${hex(StartAddress)} - ${hex(EndAddress)}]`);\n    }\n}\n\nfunction in_nursery(Addr) {\n    init();\n\n    if(Addr == undefined) {\n        logln('!in_nursery <object addr>');\n        return;\n    }\n\n    //\n    // Find 'cx' the JSContext somewhere..\n    //\n\n    if(Context == undefined) {\n        const CurrentThread = host.currentThread;\n        for(const Frame of CurrentThread.Stack.Frames) {\n            const Parameters = Frame.Parameters;\n            if(Parameters == undefined) {\n                continue;\n            }\n\n            Context = Parameters.cx;\n            if(Context == undefined ||\n            Context.targetType.toString() != 'JSContext *') {\n                continue;\n            }\n\n            logln(`Caching JSContext @${hex(Context.address)} for next times.`);\n            break;\n        }\n    } else {\n        logln(`Using previously cached JSContext @${hex(Context.address)}`);\n    }\n\n    if(Context == undefined) {\n        logln('Could not locate a JSContext in this call-stack.');\n        return;\n    }\n\n    //\n    // Get the Nursery.\n    //\n\n    const Nursery = Context.runtime_.value.gc.nursery_.value;\n    const NurseryChunk = host.createPointerObject(\n        0,\n        Module,\n        'js::NurseryChunk*'\n    );\n    const ChunkUseableSize = NurseryChunk.data.targetType.size;\n\n    //\n    // Dump some stats about the Nursery.\n    //\n\n    dump_nursery_stats(Nursery);\n    logln('-------');\n\n    //\n    // Iterate through the chunk regions.\n    //\n\n    const Chunks = Nursery.chunks_;\n    let FoundChunk = undefined;\n    for(let Idx = 0; Idx < Chunks.mLength; Idx++) {\n        const Chunk = Chunks.mBegin[Idx];\n        const StartAddress = Chunk.data.address;\n        const EndAddress = StartAddress.add(ChunkUseableSize);\n        if(Addr.compareTo(StartAddress) >= 0 &&\n           Addr.compareTo(EndAddress) < 0) {\n               FoundChunk = Chunk;\n               break;\n           }\n    }\n\n    if(FoundChunk != undefined) {\n        logln(`${hex(Addr)} has been found in the js::NurseryChunk @${hex(FoundChunk.data.address)}!`);\n        return;\n    }\n\n    logln(`${hex(Addr)} hasn't been found be in any Nursery js::NurseryChunk.`);\n}\n\nfunction initializeScript() {\n    return [\n        new host.apiVersionSupport(1, 3),\n        new host.functionAlias(\n            smdump_jsvalue,\n            'smdump_jsvalue'\n        ),\n        new host.functionAlias(\n            smdump_jsobject,\n            'smdump_jsobject'\n        ),\n        new host.functionAlias(\n            ion_insertbp,\n            'ion_insertbp'\n        ),\n        new host.functionAlias(\n            in_nursery,\n            'in_nursery'\n        )\n    ];\n}\n"
  },
  {
    "path": "telescope/README.md",
    "content": "# telescope.js\n\n`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.\n\nIdea from [@\\_\\_awe](https://twitter.com/__awe).\n\n## Usage\n\nRun `.scriptload telescope.js` to load the script. You can invoke the telescope feature with `!telescope <addr>` or programatically via `dx @$createchain(<addr>)`.\n\n## Examples\n\n* From an x64 TTD execution trace:\n\n```text\n0:000> !telescope @rsp\n0x0000005be1ffcec0|+0x0000: 0xe1000205e1ffdd48 (Unknown)\n0x0000005be1ffcec8|+0x0008: 0x00007ff700000006 (Unknown)\n0x0000005be1ffced0|+0x0010: 0x000001fce5928840 (VirtualAlloced) -> 0x0000005be1ffd0b8 (Stack) -> 0x000001fce5928840 (VirtualAlloced) [...]\n0x0000005be1ffced8|+0x0018: 0x0000005be1ffdb68 (Stack) -> 0x000001fce5928840 (VirtualAlloced) -> 0x0000005be1ffd0b8 (Stack) -> 0x000001fce5928840 (VirtualAlloced) [...]\n0x0000005be1ffcee0|+0x0020: 0x000001fce634afa0 (VirtualAlloced) -> 0x0000000800000b50 (Unknown)\n0x0000005be1ffcee8|+0x0028: 0x00004a54b4bb11e0 (Unknown)\n0x0000005be1ffcef0|+0x0030: 0x0000000000000008 (Unknown)\n0x0000005be1ffcef8|+0x0038: 0x0000000000000000 (Unknown)\n0x0000005be1ffcf00|+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\n0x0000005be1ffcf08|+0x0048: 0x00007ff7766b4546 (js.exe (.text)) -> test    rax,rax ; je      js!mozilla::Vector<char *,0,js::TempAllocPolicy>::growStorageBy+0x395 (00007ff7`766b4805)\n@$telescope(@rsp)\n```\n\n* Accessing the chain programatically via `createchain`:\n\n```text\n0:000> dx @$createchain(0x0000005be1ffcf08)\n@$createchain(0x0000005be1ffcf08)                 : 0x00007ff7766b4546 (js.exe (.text)) -> test    rax,rax ; je      js!mozilla::Vector<char *,0,js::TempAllocPolicy>::growStorageBy+0x395 (00007ff7`766b4805)\n    [0x0]            : 0x00007ff7766b4546 (js.exe (.text))\n    [0x1]            : test    rax,rax ; je      js!mozilla::Vector<char *,0,js::TempAllocPolicy>::growStorageBy+0x395 (00007ff7`766b4805)\n\n0:000> dx -r1 @$createchain(0x0000005be1ffcf08)[0]\n@$createchain(0x0000005be1ffcf08)[0]                 : 0x00007ff7766b4546 (js.exe (.text))\n    Addr             : 0x5be1ffcf08\n    Value            : 0x7ff7766b4546\n    AddrRegion       : Stack rw-\n    ValueRegion      : Image C:\\work\\codes\\blazefox\\js-release\\js.exe (.text) r-x\n    Name             : js.exe (.text)\n    Last             : false\n\n0:000> dx -r1 @$createchain(0x0000005be1ffcf08)[1]\n@$createchain(0x0000005be1ffcf08)[1]                 : test    rax,rax ; je      js!mozilla::Vector<char *,0,js::TempAllocPolicy>::growStorageBy+0x395 (00007ff7`766b4805)\n    Addr             : 0x7ff7766b4546\n    Value            : 0x2b6840fc08548\n    AddrRegion       : Image C:\\work\\codes\\blazefox\\js-release\\js.exe (.text) r-x\n    Name             : Unknown\n    Last             : true\n```\n\n* From an x86 live-session:\n\n```text\n0:001> !telescope @esp\n0x00d7ff44|+0x0000: 0x77dcb3a9 (ntdll.dll (.text)) -> jmp     ntdll!DbgUiRemoteBreakin+0x42 (77dcb3b2) ; xor     eax,eax\n0x00d7ff48|+0x0004: 0x1911c0a3 (Unknown)\n0x00d7ff4c|+0x0008: 0x77dcb370 (ntdll.dll (.text)) -> push    8 ; push    offset ntdll!QueryRegistryValue+0x13d2 (77e29538)\n0x00d7ff50|+0x000c: 0x77dcb370 (ntdll.dll (.text)) -> push    8 ; push    offset ntdll!QueryRegistryValue+0x13d2 (77e29538)\n0x00d7ff54|+0x0010: 0x00000000 (Unknown)\n0x00d7ff58|+0x0014: 0x00d7ff48 (Stack) -> 0x1911c0a3 (Unknown)\n0x00d7ff5c|+0x0018: 0x00000000 (Unknown)\n0x00d7ff60|+0x001c: 0x00d7ffcc (Stack) -> 0x00d7ffe4 (Stack) -> 0xffffffff (Unknown)\n0x00d7ff64|+0x0020: 0x77d986d0 (ntdll.dll (.text)) -> mov     edi,edi ; push    ebp\n0x00d7ff68|+0x0024: 0x6e24aaeb (Unknown)\n@$telescope(@esp)\n```\n\n* From an x64 kernel live-session\n\n```\nkd> !telescope 0xfffff8000d2dca78\n0xfffff8000d2dca78|+0x0000: 0x0000000000000000 (Unknown)\n0xfffff8000d2dca80|+0x0008: 0x0000000000000000 (Unknown)\n0xfffff8000d2dca88|+0x0010: 0x0000000000000000 (Unknown)\n0xfffff8000d2dca90|+0x0018: 0xfffff8000d03e030 (Image ntkrnlmp.exe (.text)) -> sub     rsp,28h ; and     qword ptr [rsp+28h],0\n0xfffff8000d2dca98|+0x0020: 0x0000000000000000 (Unknown)\n0xfffff8000d2dcaa0|+0x0028: 0x0000000000000000 (Unknown)\n0xfffff8000d2dcaa8|+0x0030: 0xfffff8000d2d9e48 (Image ntkrnlmp.exe (CACHEALI)) -> 0xfffff8000d2dcaa8 (Image ntkrnlmp.exe (CACHEALI)) [...]\n0xfffff8000d2dcab0|+0x0038: 0xfffff8000d2d9e48 (Image ntkrnlmp.exe (CACHEALI)) -> 0xfffff8000d2dcaa8 (Image ntkrnlmp.exe (CACHEALI)) -> 0xfffff8000d2d9e48 (Image ntkrnlmp.exe (CACHEALI)) [...]\n0xfffff8000d2dcab8|+0x0040: 0x0000000000000000 (Unknown)\n0xfffff8000d2dcac0|+0x0048: 0x0000000000000000 (Unknown)\n@$telescope(0xfffff8000d2dca78)\n```\n"
  },
  {
    "path": "telescope/telescope.js",
    "content": "// Axel '0vercl0k' Souchet - 7th December 2018\n\n'use strict';\n\nconst log = host.diagnostics.debugLog;\nconst logln = p => host.diagnostics.debugLog(p + '\\n');\n\n//\n// Config variables.\n//\n\n// This is the number of lines the !telescope command displays.\nconst DefaultNumberOfLines = 10;\n\n// This is the number of instructions to disassemble when a code pointer is encountered.\nconst DefaultNumberOfInstructions = 3;\n\n// This is the maximum number of characters displayed for strings in the !telescope output.\nconst DefaultMaxStringLength = 15;\n\n//\n// Utility functions.\n//\n\nfunction ReadU64(Addr) {\n    let Value = null;\n    try {\n        Value = host.memory.readMemoryValues(\n            Addr, 1, 8\n        )[0];\n    } catch (e) {\n    }\n\n    return Value;\n}\n\nfunction ReadU32(Addr) {\n    let Value = null;\n    try {\n        Value = host.memory.readMemoryValues(\n            Addr, 1, 4\n        )[0];\n    } catch (e) {\n    }\n\n    return Value;\n}\n\nfunction ReadU16(Addr) {\n    let Value = null;\n    try {\n        Value = host.memory.readMemoryValues(\n            Addr, 1, 2\n        )[0];\n    } catch (e) {\n    }\n\n    return Value;\n}\n\nfunction ReadString(Addr, MaxLength) {\n    let Value = null;\n    try {\n        Value = host.memory.readString(Addr);\n    } catch (e) {\n        return null;\n    }\n\n    if (Value.length > MaxLength) {\n        return Value.substr(0, MaxLength);\n    }\n\n    return Value;\n}\n\nfunction ReadWideString(Addr) {\n    let Value = null;\n    try {\n        Value = host.memory.readWideString(Addr);\n    } catch (e) {\n    }\n\n    return Value;\n}\n\nfunction Disassemble(Addr) {\n    const Code = host.namespace.Debugger.Utility.Code;\n    const Disassembler = Code.CreateDisassembler(\n        PointerSize == 8 ? 'X64' : 'X86'\n    );\n    const Instrs = Array.from(Disassembler.DisassembleInstructions(Addr).Take(\n        DefaultNumberOfInstructions\n    ));\n\n    return Instrs.map(\n\n        //\n        // Clean up the assembly.\n        // Turn the below:\n        //   'mov     rbx,qword ptr [00007FF8D3525660h] ; test    rbx,rbx ; je     00007FF8D34FC2EB'\n        // Into:\n        //   'mov rbx,qword ptr [00007FF8D3525660h] ; test rbx,rbx ; je 00007FF8D34FC2EB'\n        //\n\n        p => p.toString().replace(/[ ]+/g, ' ')\n    ).join(' ; ');\n}\n\nfunction FormatU64(Addr) {\n    return '0x' + Addr.toString(16).padStart(16, '0');\n}\n\nfunction FormatU32(Addr) {\n    return '0x' + Addr.toString(16).padStart(8, '0');\n}\n\nfunction FormatString(Str) {\n    if (Str.length > DefaultMaxStringLength) {\n        return Str.substr(0, DefaultMaxStringLength) + '...'\n    }\n\n    return Str;\n}\n\nfunction BitSet(Value, Bit) {\n    return Value.bitwiseAnd(Bit).compareTo(0) != 0;\n}\n\n//\n// Initialization / global stuff.\n//\n\nlet Initialized = false;\nlet ReadPtr = null;\nlet PointerSize = null;\nlet FormatPtr = null;\nlet IsTTD = false;\nlet IsUser = false;\nlet IsKernel = false;\nlet VaSpace = [];\n\nfunction* SectionHeaders(BaseAddress) {\n    if (IsKernel && ReadU32(BaseAddress) == null) {\n\n        //\n        // If we can't read the module, then..bail :(.\n        // XXX: Fix this? Session space? Paged out?\n        //\n\n        logln('Cannot read ' + BaseAddress.toString(16) + ', skipping.');\n        return;\n    }\n\n    // 0:000> dt _IMAGE_DOS_HEADER e_lfanew\n    //   +0x03c e_lfanew : Int4B\n    const NtHeaders = BaseAddress.add(ReadU32(BaseAddress.add(0x3c)));\n    // 0:000> dt _IMAGE_NT_HEADERS64 FileHeader\n    //    +0x004 FileHeader : _IMAGE_FILE_HEADER\n    // 0:000> dt _IMAGE_FILE_HEADER NumberOfSections SizeOfOptionalHeader\n    //    +0x002 NumberOfSections : Uint2B\n    //    +0x010 SizeOfOptionalHeader : Uint2B\n    const NumberOfSections = ReadU16(NtHeaders.add(0x4 + 0x2));\n    const SizeOfOptionalHeader = ReadU16(NtHeaders.add(0x4 + 0x10));\n    // 0:000> dt _IMAGE_NT_HEADERS64 OptionalHeader\n    //   +0x018 OptionalHeader : _IMAGE_OPTIONAL_HEADER64\n    const OptionalHeader = NtHeaders.add(0x18);\n    const SectionHeaders = OptionalHeader.add(SizeOfOptionalHeader);\n    // 0:000> ?? sizeof(_IMAGE_SECTION_HEADER)\n    // unsigned int64 0x28\n    const SizeofSectionHeader = 0x28;\n    for (let Idx = 0; Idx < NumberOfSections; Idx++) {\n        const SectionHeader = SectionHeaders.add(\n            Idx.multiply(SizeofSectionHeader)\n        );\n        // 0:000> dt _IMAGE_SECTION_HEADER Name\n        //    +0x000 Name             : [8] UChar\n        const Name = ReadString(SectionHeader, 8);\n        // 0:000> dt _IMAGE_SECTION_HEADER VirtualAddress\n        //    +0x00c VirtualAddress : Uint4B\n        const Address = BaseAddress.add(\n            ReadU32(SectionHeader.add(0xc))\n        );\n        // 0:000> dt _IMAGE_SECTION_HEADER SizeOfRawData\n        //    +0x08 Misc : Uint4B\n        // XXX: Take care of alignment?\n        const VirtualSize = ReadU32(SectionHeader.add(0x08));\n        // 0:000> dt _IMAGE_SECTION_HEADER Characteristics\n        //    +0x024 Characteristics : Uint4B\n        const Characteristics = ReadU32(SectionHeader.add(0x24));\n        const Properties = [\n            '-',\n            '-',\n            '-'\n        ];\n\n        // The section can be read.\n        const IMAGE_SCN_MEM_READ = host.Int64(0x40000000);\n        if (BitSet(Characteristics, IMAGE_SCN_MEM_READ)) {\n            Properties[0] = 'r';\n        }\n\n        if (IsKernel) {\n            const IMAGE_SCN_MEM_DISCARDABLE = host.Int64(0x2000000);\n            if (BitSet(Characteristics, IMAGE_SCN_MEM_DISCARDABLE)) {\n                Properties[0] = '-';\n            }\n        }\n\n        // The section can be written to.\n        const IMAGE_SCN_MEM_WRITE = host.Int64(0x80000000);\n        if (Characteristics.bitwiseAnd(IMAGE_SCN_MEM_WRITE).compareTo(0) != 0) {\n            Properties[1] = 'w';\n        }\n\n        // The section can be executed as code.\n        const IMAGE_SCN_MEM_EXECUTE = host.Int64(0x20000000);\n        if (Characteristics.bitwiseAnd(IMAGE_SCN_MEM_EXECUTE).compareTo(0) != 0) {\n            Properties[2] = 'x';\n        }\n\n        yield new _Region(\n            Address,\n            VirtualSize,\n            Name,\n            Properties.join('')\n        );\n    }\n}\n\nfunction HandleTTD() {\n    const CurrentSession = host.currentSession;\n\n    //\n    // Grab addressable chunks.\n    //\n\n    logln('Populating the VA space with TTD.Data.Heap..');\n    const CurrentThread = host.currentThread;\n    const Position = CurrentThread.TTD.Position;\n    const Chunks = CurrentSession.TTD.Data.Heap().Where(\n        p => p.TimeStart.compareTo(Position) < 0 &&\n            p.Action == 'Alloc'\n    );\n\n    for (const Chunk of Chunks) {\n        VaSpace.push(new _Region(\n            Chunk.Address,\n            Chunk.Size,\n            'Heap',\n            'rw-'\n        ));\n    }\n\n    //\n    // Grab virtual allocated memory regions.\n    //\n\n    logln('Populating the VA space with VirtualAllocated regions..');\n    const VirtualAllocs = CurrentSession.TTD.Calls(\n        'kernelbase!VirtualAlloc'\n    ).Where(\n        p => p.TimeStart.compareTo(Position) < 0\n    );\n\n    for (const VirtualAlloc of VirtualAllocs) {\n        VaSpace.push(new _Region(\n            VirtualAlloc.ReturnValue,\n            VirtualAlloc.Parameters[1],\n            'VirtualAlloced',\n            // XXX: parse access\n            'rw-'\n        ));\n    }\n\n    //\n    // Grab mapped view regions.\n    //\n\n    logln('Populating the VA space with MappedViewOfFile regions..');\n    const MapViewOfFiles = CurrentSession.TTD.Calls(\n        'kernelbase!MapViewOfFile'\n    ).Where(\n        p => p.TimeStart.compareTo(Position) < 0\n    );\n\n    for (const MapViewOfFile of MapViewOfFiles) {\n        VaSpace.push(new _Region(\n            MapViewOfFile.ReturnValue,\n            host.Int64(0x1000),\n            'MappedView',\n            // XXX: parse access\n            'rw-'\n        ));\n    }\n}\n\nfunction HandleUser() {\n\n    //\n    // Enumerate the modules.\n    //\n\n    logln('Populating the VA space with modules..');\n    const CurrentProcess = host.currentProcess;\n    for (const Module of CurrentProcess.Modules) {\n\n        //\n        // Iterate over the section headers of the module.\n        //\n\n        for (const Section of SectionHeaders(Module.BaseAddress)) {\n            VaSpace.push(new _Region(\n                Section.BaseAddress,\n                Section.Size,\n                'Image ' + Module.Name + ' (' + Section.Name + ')',\n                Section.Properties\n            ));\n        }\n\n        //\n        // Add a catch all in case a pointer points inside the PE but not\n        // inside any sections (example of this is the PE header).\n        //\n\n        VaSpace.push(new _Region(\n            Module.BaseAddress,\n            Module.Size,\n            'Image ' + Module.Name,\n            'r--'\n        ));\n    }\n\n    //\n    // Enumerates the TEBs and the stacks.\n    //\n\n    logln('Populating the VA space with TEBs & thread stacks..');\n    for (const Thread of CurrentProcess.Threads) {\n        const Teb = Thread.Environment.EnvironmentBlock;\n\n        //\n        // TEB!\n        //\n        // In the case where you have broken `ntdll` symbols, you might not have\n        // the definition of the `_TEB` structure. In this case, the structured\n        // `Teb` object above is undefined (like in issues #2). So we try to be resilient\n        // against that in the below.\n        //\n\n        if (Teb == undefined) {\n            const General = host.namespace.Debugger.State.PseudoRegisters.General;\n            VaSpace.push(new _Region(\n                General.teb.address,\n                host.Int64(0x100),\n                'Teb of ' + Thread.Id.toString(16),\n                'rw-'\n            ));\n\n            continue;\n        }\n\n        VaSpace.push(new _Region(\n            Teb.address,\n            Teb.targetType.size,\n            'Teb of ' + Thread.Id.toString(16),\n            'rw-'\n        ));\n\n        //\n        // Stacks!\n        //\n\n        const StackBase = Teb.NtTib.StackBase.address;\n        const StackLimit = Teb.NtTib.StackLimit.address;\n        VaSpace.push(new _Region(\n            StackLimit,\n            StackBase.subtract(StackLimit),\n            'Stack',\n            'rw-'\n        ));\n    }\n\n    //\n    // Get the PEB. Keep in mind we can run into the same symbol problem with the\n    // PEB - so account for that.\n    //\n\n    logln('Populating the VA space with the PEB..');\n    const Peb = CurrentProcess.Environment.EnvironmentBlock;\n\n    if (Peb == undefined) {\n        const General = host.namespace.Debugger.State.PseudoRegisters.General;\n        VaSpace.push(new _Region(\n            General.peb.address,\n            host.Int64(0x1000),\n            'Peb',\n            'rw-'\n        ));\n\n        logln(`/!\\\\ Several regions have been skipped because nt!_TEB / nt!_PEB aren't available in your symbols.`);\n    } else {\n        VaSpace.push(new _Region(\n            Peb.address,\n            Peb.targetType.size,\n            'Peb',\n            'rw-'\n        ));\n    }\n}\n\nfunction HandleKernel() {\n\n    //\n    // Enumerate the kernel modules.\n    //\n\n    logln('Populating the VA space with kernel modules..');\n    const CurrentSession = host.currentSession;\n    const SystemProcess = CurrentSession.Processes.First(\n        p => p.Name == 'System'\n    );\n\n    const MmUserProbeAddress = ReadPtr(\n        host.getModuleSymbolAddress('nt', 'MmUserProbeAddress')\n    );\n\n    const KernelModules = SystemProcess.Modules.Where(\n        p => p.BaseAddress.compareTo(MmUserProbeAddress) > 0\n    );\n\n    for (const Module of KernelModules) {\n\n        //\n        // Iterate over the section headers of the module.\n        //\n\n        for (const Section of SectionHeaders(Module.BaseAddress)) {\n            VaSpace.push(new _Region(\n                Section.BaseAddress,\n                Section.Size,\n                'Driver ' + Module.Name + ' (' + Section.Name + ')',\n                Section.Properties\n            ));\n        }\n\n        //\n        // Add a catch all in case a pointer points inside the PE but not\n        // inside any sections (example of this is the PE header).\n        //\n\n        VaSpace.push(new _Region(\n            Module.BaseAddress,\n            Module.Size,\n            'Driver ' + Module.Name,\n            'r--'\n        ));\n    }\n}\n\nfunction InitializeVASpace() {\n    if (IsUser) {\n        HandleUser();\n    }\n\n    if (IsTTD) {\n\n        //\n        // If we have a TTD target, let's do some more work.\n        //\n\n        HandleTTD();\n    }\n\n    if (IsKernel) {\n        HandleKernel();\n    }\n}\n\nfunction InitializeWrapper(Funct) {\n    return Arg => {\n        if (!Initialized) {\n            const CurrentSession = host.currentSession;\n\n            //\n            // Initialize the ReadPtr function according to the PointerSize.\n            //\n\n            PointerSize = CurrentSession.Attributes.Machine.PointerSize;\n            ReadPtr = PointerSize.compareTo(8) == 0 ? ReadU64 : ReadU32;\n            FormatPtr = PointerSize.compareTo(8) == 0 ? FormatU64 : FormatU32;\n            const TargetAttributes = CurrentSession.Attributes.Target;\n            IsTTD = TargetAttributes.IsTTDTarget;\n            IsUser = TargetAttributes.IsUserTarget;\n            IsKernel = TargetAttributes.IsKernelTarget;\n\n            //\n            // One time initialization!\n            //\n\n            Initialized = true;\n        }\n\n        //\n        // Once initialization is done, call into the function.\n        //\n\n        return Funct(Arg);\n    };\n}\n\n\n//\n// The meat!\n//\n\nclass _Region {\n    constructor(BaseAddress, Size, Name, Properties) {\n        this.Name = Name;\n        this.BaseAddress = BaseAddress;\n        this.EndAddress = this.BaseAddress.add(Size);\n        this.Size = Size;\n        this.Properties = Properties;\n        this.Executable = false;\n        this.Readable = false;\n        this.Writeable = false;\n        if (Properties.indexOf('r') != -1) {\n            this.Readable = true;\n        }\n\n        if (Properties.indexOf('w') != -1) {\n            this.Writeable = true;\n        }\n\n        if (Properties.indexOf('x') != -1) {\n            this.Executable = true;\n        }\n\n    }\n\n    In(Addr) {\n        const InBounds = Addr.compareTo(this.BaseAddress) >= 0 &&\n            Addr.compareTo(this.EndAddress) < 0;\n        return InBounds;\n    }\n\n    toString() {\n        const Prop = [\n            this.Readable ? 'r' : '-',\n            this.Writeable ? 'w' : '-',\n            this.Executable ? 'x' : '-'\n        ];\n\n        return this.Name + ' ' + Prop.join('');\n    }\n}\n\nfunction AddressToRegion(Addr) {\n\n    //\n    // Map the address space with VA regions.\n    //\n\n    const Hits = VaSpace.filter(\n        p => p.In(Addr)\n    );\n\n    //\n    // Now, let's get the most precise region information by ordering\n    // the hits by size.\n    //\n\n    const OrderedHits = Hits.sort(\n        (a, b) => a.Size.compareTo(b.Size)\n    );\n\n    //\n    // Return the most precise information we have!\n    //\n\n    return OrderedHits[0];\n}\n\nclass _ChainEntry {\n    constructor(Addr, Value) {\n        this.Addr = Addr;\n        this.Value = Value;\n        this.AddrRegion = AddressToRegion(this.Addr);\n        this.ValueRegion = AddressToRegion(this.Value);\n        if (this.ValueRegion == undefined) {\n            this.Name = 'Unknown';\n        } else {\n\n            //\n            // Just keep the file name and strips off the path.\n            //\n\n            this.Name = this.ValueRegion.Name;\n            this.Name = this.Name.substring(this.Name.lastIndexOf('\\\\') + 1);\n        }\n        this.Last = false;\n    }\n\n    Equals(Entry) {\n        return this.Addr.compareTo(Entry.Addr) == 0;\n    }\n\n    toString() {\n        const S = FormatPtr(this.Value) + ' (' + this.Name + ')';\n        if (!this.Last) {\n            return S;\n        }\n\n        //\n        // We only provide disassembly if we know that the code is executeable.\n        // And in order to know that, we need to have a valid `AddrRegion`.\n        //\n\n        if (this.AddrRegion != undefined && this.AddrRegion.Executable) {\n            return Disassemble(this.Addr);\n        }\n\n        //\n        // If we have a string stored in a heap allocation what happens is the following:\n        //   - The extension does not know about heap, so `AddrRegion` for such a pointer\n        //   would be `undefined`.\n        //   - Even though it is undefined, we would like to display a string if there is any,\n        //   instead of just the first qword.\n        // So to enable the scenario to work, we allow to enter the below block with an `AddrRegion`\n        // that is undefined.\n        //\n\n        if (this.AddrRegion == undefined || this.AddrRegion.Readable) {\n\n            const IsPrintable = p => {\n                return p != null &&\n                    // XXX: ugly AF.\n                    p.match(/^[a-z0-9!\"#$%&'()*+,/\\\\.:;<=>?@\\[\\] ^_`{|}~-]+$/i) != null &&\n                    p.length > 5\n            };\n\n            //\n            // Maybe it points on a unicode / ascii string?\n            //\n\n            const Ansi = ReadString(this.Addr);\n\n            if (IsPrintable(Ansi)) {\n                return `${FormatPtr(this.Addr)} (Ascii(${FormatString(Ansi)}))`;\n            }\n\n            const Wide = ReadWideString(this.Addr);\n            if (IsPrintable(Wide)) {\n                return `${FormatPtr(this.Addr)} (Unicode(${FormatString(Wide)}))`;\n            }\n        }\n\n        //\n        // If we didn't find something better, fallback to the regular\n        // output.\n        //\n\n        return S;\n    }\n}\n\nclass _Chain {\n    constructor(Addr) {\n        this.__Entries = [];\n        this.__HasCycle = false;\n        this.__Addr = Addr;\n        while (this.FollowPtr()) { };\n        this.__Length = this.__Entries.length;\n\n        //\n        // Tag the last entry as 'last'.\n        //\n\n        if (this.__Length >= 1) {\n            this.__Entries[this.__Length - 1].Last = true;\n        }\n    }\n\n    FollowPtr() {\n\n        //\n        // Attempt to follow the pointer.\n        //\n\n        const Value = ReadPtr(this.__Addr);\n        if (Value == null) {\n\n            //\n            // We are done following pointers now!\n            //\n\n            return false;\n        }\n\n        //\n        // Let's build an entry and evaluate what we want to do with it.\n        //\n\n        const Entry = new _ChainEntry(this.__Addr, Value);\n        const DoesEntryExist = this.__Entries.find(\n            p => p.Equals(Entry)\n        );\n\n        if (DoesEntryExist) {\n\n            //\n            // If we have seen this Entry before, it means there's a cycle\n            // and we will stop there.\n            //\n\n            this.__HasCycle = true;\n            return false;\n        }\n\n        //\n        // This Entry is of interest, so let's add it in our list.\n        //\n\n        this.__Entries.push(Entry);\n        this.__Addr = Value;\n        return true;\n    }\n\n    toString() {\n        if (this.__Entries.length == 0) {\n            return '';\n        }\n\n        //\n        // Iterate over the chain.\n        //\n\n        let S = this.__Entries.join(' -> ');\n\n        //\n        // Add a little something if we have a cycle so that the user knows.\n        //\n\n        if (this.__HasCycle) {\n            S += ' [...]';\n        }\n\n        return S;\n    }\n\n    *[Symbol.iterator]() {\n        for (const Entry of this.__Entries) {\n            yield Entry;\n        }\n    }\n}\n\nfunction CreateChain(Addr) {\n\n    //\n    // Initialize the VA space.\n    //\n\n    InitializeVASpace();\n\n    const Chain = new _Chain(Addr);\n    VaSpace = [];\n    return Chain;\n}\n\nfunction Telescope(Addr) {\n    if (Addr == undefined) {\n        logln('!telescope <addr>');\n        return;\n    }\n\n    //\n    // Initialize the VA space.\n    //\n\n    InitializeVASpace();\n\n    const CurrentSession = host.currentSession;\n    const Lines = DefaultNumberOfLines;\n    const PointerSize = CurrentSession.Attributes.Machine.PointerSize;\n    const FormatOffset = p => '0x' + p.toString(16).padStart(4, '0');\n\n    for (let Idx = 0; Idx < Lines; Idx++) {\n        const Offset = PointerSize.multiply(Idx);\n        const CurAddr = Addr.add(Offset);\n        const Chain = new _Chain(CurAddr);\n        const Header = FormatPtr(CurAddr) + '|+' + FormatOffset(Offset);\n        logln(Header + ': ' + Chain.toString());\n    }\n\n    VaSpace = [];\n}\n\nfunction initializeScript() {\n    return [\n        new host.apiVersionSupport(1, 3),\n        new host.functionAlias(\n            InitializeWrapper(Telescope),\n            'telescope'\n        ),\n        new host.functionAlias(\n            InitializeWrapper(CreateChain),\n            'createchain'\n        )\n    ];\n}\n"
  }
]