[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Tim Misiak\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": "README.md",
    "content": "# WinDbgCookbook\nThis is a repo for small, useful scripts, extensions, and debugger data model \"dx\" queries.\n\nFeel free to add your own scripts or update any of the scripts here. If you add a new script, just add a line in this readme file giving a summary of your script.\n\n# Modules\n\nFind if a module called \"dbgeng.dll\" has any imports called \"RegGetValue\".\n\n```dx @$curprocess.Modules[\"dbgeng.dll\"].Contents.Imports.SelectMany(x => x.Functions).Where(x => x.ToDisplayString().Contains(\"RegGetValue\"))```\n\nSet a breakpoint on every export of \"symsrv.dll\". (The ```.Where(x => false)``` makes the command silent and makes sure everything gets evaluated instead of stopping at 100 breakpoints)\n\n```dx @$curprocess.Modules[\"symsrv\"].Contents.Exports.Select(x => Debugger.Utility.Control.ExecuteCommand(\"bp \" + x.CodeAddress.ToDisplayString(\"x\"))).Where(x => false)```\n\nGet a table of every import from a module \"dbghelp\":\n\n```dx -g @$curprocess.Modules[\"dbghelp\"].Contents.Imports.SelectMany(m => m.Functions.Select(f => new {Mod = m, Func = f})), 1000```\n\nFind any modules that import module \"foo.dll\":\n\n```dx @$curprocess.Modules.Where(m => m.Contents.Imports.Any(x => x.ModuleName.ToLower() == \"foo.dll\"))```\n\n# Threads\n\nFind any threads that are currently executing for a module called \"mymodule.dll\" or \"mymodule.exe\"\n\n```dx @$curprocess.Threads.Where(x => x.Stack.Frames.Any(f => f.ToDisplayString().Contains(\"mymodule!\")))```\n\n# Environment variables\n\nFind an environment variable by name\n\n```\ndx @$getEnvVar=(var) => @$curprocess.Attributes.Environment.Variables.Where(x => x.ToLower().StartsWith(var.ToLower() + \"=\"))\n```\n\nExample:\n\n```\n0:000> dx @$getEnvVar(\"tmp\")\n@$getEnvVar(\"tmp\")                \n    [0x0]            : TMP=C:\\Users\\tmisiak\\AppData\\Local\\Temp\n```\n\n# .NET Managed objects with \"dx\"\n\nYou can \"cast\" a managed object pointer to \"System.Object\" and dx will give you the full object information.\n\n```\ndx (System_Private_CoreLib!System.Object *)0x1234\n```\n\n# stackCollector.js\n\nThis script adds a function that can be called from inside a breakpoint condition to capture the stack trace at the time the breakpoint was hit, and add it to a call graph. For instance, load the script and then set a breakpoint like:\n\n```bp /w \"@$scriptContents.onBreakpoint(), false\" kernelbase!ReadFile```\n\nThen run the program and later you can view the graph using a command like this:\n\n```\n0:007> dx -r3 @$scriptContents.stackRoot\n@$scriptContents.stackRoot                 : undefined - 0\n    KERNELBASE!ReadFile : KERNELBASE!ReadFile - 173\n        ucrtbase!_read_nolock : ucrtbase!_read_nolock - 2\n            ucrtbase!_read   : ucrtbase!_read - 2\n        shcore!CFileStream::Read : shcore!CFileStream::Read - 156\n            XmlLite!CharacterSource::Bytes::ReadMore : XmlLite!CharacterSource::Bytes::ReadMore - 70\n            dbgeng!ConvertStreamToUnicode : dbgeng!ConvertStreamToUnicode - 86\n        msvcrt!read_nolock : msvcrt!read_nolock - 1\n            msvcrt!read      : msvcrt!read - 1\n        dbghelp!IStreamFileWinAPI::Read : dbghelp!IStreamFileWinAPI::Read - 4\n            dbghelp!MSF_HB::readPnOffCb : dbghelp!MSF_HB::readPnOffCb - 4\n        dbgeng!ReadImageData : dbgeng!ReadImageData - 10\n            dbgeng!IMAGE_HEADER_INFO::Read : dbgeng!IMAGE_HEADER_INFO::Read - 5\n            dbgeng!IMAGE_HEADER_INFO::ReadLoadConfigDir : dbgeng!IMAGE_HEADER_INFO::ReadLoadConfigDir - 1\n            dbgeng!IMAGE_HEADER_INFO::ReadDebugDir : dbgeng!IMAGE_HEADER_INFO::ReadDebugDir - 4\n```\n\n# watchForStackCorruption.js\n\nThis script adds a function that can watch for stack corruptions in a function on the target.\n\nUse it by running:\n\n```\n0:000> dx @$scriptContents.watchFunctionForStackCorruption(\"OverflowFunc\")\n@$scriptContents.watchFunctionForStackCorruption(\"OverflowFunc\")\n0:000> g\nStack corruption detected!\nWindowsConsoleApp!OverflowFunc+0x3b:\n00007ff7`56cb117b f3aa            rep stos byte ptr [rdi]\n```\n\n# ttdUtil.js\n\nFind all calls to exports of a specific module for a TTD trace:\n\n```\n0:000> dx -g @$scriptContents.AllExportCalls(\"symsrv\")\n===========================================================================================================================================================================================================================================================================================================================\n=           = (+) EventType = (+) ThreadId = (+) UniqueThreadId = (+) TimeStart  = (+) TimeEnd    = (+) Function                               = (+) FunctionAddress = (+) ReturnAddress = (+) ReturnValue = (+) Parameters = (+) SystemTimeStart                          = (+) SystemTimeEnd                            =\n===========================================================================================================================================================================================================================================================================================================================\n= [0x0]     - 0x0           - 0x8610       - 0x2                - 10A0BD:77      - 10A0BF:BC      - symsrv!SymbolServerSetOptionsW             - 0x7ffa9d5627c0      - 0x7ffa340b876a    - 1               - {...}          - Thursday, September 15, 2022 23:17:59.836    - Thursday, September 15, 2022 23:17:59.836    =\n= [0x1]     - 0x0           - 0x8610       - 0x2                - 10A0C0:15      - 10A0C0:A4      - symsrv!SymbolServerSetOptionsW             - 0x7ffa9d5627c0      - 0x7ffa340b87b1    - 1               - {...}          - Thursday, September 15, 2022 23:17:59.836    - Thursday, September 15, 2022 23:17:59.836    =\n= [0x2]     - 0x0           - 0x8610       - 0x2                - 10A0CA:15      - 10A0CA:A3      - symsrv!SymbolServerSetOptionsW             - 0x7ffa9d5627c0      - 0x7ffa340b87b1    - 1               - {...}          - Thursday, September 15, 2022 23:17:59.836    - Thursday, September 15, 2022 23:17:59.836    =\n= [0x3]     - 0x0           - 0x8610       - 0x2                - 10A0CE:15      - 10A0CE:9F      - symsrv!SymbolServerSetOptionsW             - 0x7ffa9d5627c0      - 0x7ffa340b87b1    - 1               - {...}          - Thursday, September 15, 2022 23:17:59.836    - Thursday, September 15, 2022 23:17:59.836    =\n= [0x4]     - 0x0           - 0x8610       - 0x2                - 10A0D0:18      - 10A0D1:0       - symsrv!SymbolServerGetOptionData           - 0x7ffa9d562db0      - 0x7ffa340b8857    - 0               - {...}          - Thursday, September 15, 2022 23:17:59.836    - Thursday, September 15, 2022 23:17:59.836    =\n```\n\n# Javascript tips\n\nWith `dx`, you can often index into a collection dynamically via multiple keys. For instance, ```@$curprocess.Modules[0]``` works as well as ```@$curprocess.Modules[\"foo.dll\"]``` and ```@$curprocess.Modules[\"foo\"]```. The same thing doesn't work directly from JavaScript because it doesn't support dynamic indexing. Instead the data model projects this in as `.getValueAt`, so you can do:\n\n```\nhost.currentProcess.Modules.getValueAt(\"foo\")\n```\n\n# Other collections of scripts and queries\n\n* Official WinDbg samples: https://github.com/microsoft/WinDbg-Samples\n* Yarden Shafir's WinDbg Scripts: https://github.com/yardenshafir/WinDbg_Scripts\n* Hugsy's WinDbg JS scripts: https://github.com/hugsy/windbg_js_scripts\n* Axel Souchet's scripts: https://github.com/0vercl0k/windbg-scripts\n"
  },
  {
    "path": "dependencyTree.js",
    "content": "﻿\"use strict\";\r\n\r\nfunction initializeScript()\r\n{\r\n    return [new host.apiVersionSupport(1, 7)];\r\n}\r\n\r\nfunction __getModByName(modName)\r\n{\r\n    modName = modName.toLowerCase();\r\n    var matches = host.currentProcess.Modules.Where(function (x) { return x.Name.toLowerCase() == modName; });\r\n    if (matches.Count() == 0)\r\n    {\r\n        matches = host.currentProcess.Modules.Where(function (x) { return x.Name.toLowerCase().includes(modName); });\r\n    }\r\n    if (matches.Count() == 0)\r\n    {\r\n        return undefined;\r\n    }\r\n    return matches.First();\r\n}\r\n\r\nclass dependencyWalker\r\n{\r\n    constructor(modName)\r\n    {\r\n        this.__modName = modName;\r\n    }\r\n\r\n    \r\n\r\n    *[Symbol.iterator]()\r\n    {\r\n        var rootMod = __getModByName(this.__modName);\r\n        if (rootMod === undefined)\r\n        {\r\n            return;\r\n        }\r\n        for (var mod of rootMod.Contents.Imports)\r\n        {\r\n            yield new dependencyWalker(mod.ModuleName);\r\n        }\r\n    }\r\n\r\n    toString()\r\n    {\r\n        return this.__modName;\r\n    }\r\n}\r\n\r\nclass reverseDependencyWalker\r\n{\r\n    constructor(modName)\r\n    {\r\n        this.__modName = modName;\r\n    }\r\n\r\n    getModByName(modName)\r\n    {\r\n        modName = modName.toLowerCase();\r\n        var matches = host.currentProcess.Modules.Where(function (x) { return x.Name.toLowerCase() == modName; });\r\n        if (matches.Count() == 0)\r\n        {\r\n            matches = host.currentProcess.Modules.Where(function (x) { return x.Name.toLowerCase().includes(modName); });\r\n        }\r\n        if (matches.Count() == 0)\r\n        {\r\n            return undefined;\r\n        }\r\n        return matches.First();\r\n    }\r\n\r\n    *[Symbol.iterator]()\r\n    {\r\n        var rootMod = this.getModByName(this.__modName);\r\n        if (rootMod === undefined)\r\n        {\r\n            return;\r\n        }\r\n\r\n        var cleanModName = rootMod.Name;\r\n        if (cleanModName.includes(\"\\\\\"))\r\n        {\r\n            cleanModName = cleanModName.substring(cleanModName.lastIndexOf(\"\\\\\") + 1);\r\n        }\r\n        cleanModName = cleanModName.toLowerCase();\r\n\r\n        var matches = host.currentProcess.Modules.Where(function (x)\r\n            {\r\n                return x.Contents.Imports.Any(function (y)\r\n                {\r\n                    //host.diagnostics.debugLog(y.ModuleName.toLowerCase() + \"<>\" + cleanModName + \"\\r\\n\");\r\n                    return y.ModuleName == cleanModName;\r\n                });\r\n            });\r\n        for (var mod of matches)\r\n        {\r\n            yield new reverseDependencyWalker(mod.Name);\r\n        }\r\n    }\r\n\r\n    toString()\r\n    {\r\n        return this.__modName;\r\n    }\r\n}\r\n\r\nfunction dependencyTree(modName)\r\n{\r\n    return new dependencyWalker(modName);\r\n}\r\n\r\nfunction inverseDependencyTree(modName)\r\n{\r\n    return new reverseDependencyWalker(modName);\r\n}"
  },
  {
    "path": "stackCollector.js",
    "content": "﻿\"use strict\";\r\n\r\nfunction initializeScript()\r\n{\r\n    return [new host.apiVersionSupport(1, 7)];\r\n}\r\n\r\nclass StackEntry\r\n{\r\n    constructor(name)\r\n    {\r\n        this.__name = name;\r\n        this.__count = 0;\r\n    }\r\n\r\n    getOrCreateChild(name)\r\n    {\r\n        if (!(name in this))\r\n        {\r\n            this[name] = new StackEntry(name);\r\n        }\r\n        return this[name];\r\n    }\r\n\r\n    toString()\r\n    {\r\n        return this.__name + \" - \" + this.__count;\r\n    }\r\n}\r\n\r\nvar stackRoot = new StackEntry();\r\n\r\nfunction onBreakpoint()\r\n{\r\n    var node = stackRoot;\r\n    for (var frame of host.namespace.Debugger.State.DebuggerVariables.curstack.Frames)\r\n    {\r\n        var funcName = frame.toString().split(\"+\")[0].trim();\r\n        node = node.getOrCreateChild(funcName);\r\n        node.__count++;\r\n    }\r\n}\r\n\r\nfunction resetStackTraces()\r\n{\r\n    stackRoot = new StackEntry();\r\n}"
  },
  {
    "path": "ttdUtil.js",
    "content": "\"use strict\";\n\nfunction initializeScript()\n{\n    return [new host.apiVersionSupport(1, 7)];\n}\n\n\nfunction AllExportCalls(mod)\n{\n    var TTD = host.currentSession.TTD;\n    var funcs = host.currentProcess.Modules.getValueAt(mod).Contents.Exports;\n    var funcNames = funcs.Select(x => mod + \"!\" + x.Name);\n    // funcNames is an iterable, and we need to convert to an array for .apply\n    var funcNamesCopy = [...funcNames];\n    return TTD.Calls.apply(TTD, funcNamesCopy);\n}\n\nfunction AllExternalExportCalls(mod)\n{\n    var calls = AllExportCalls(mod);\n    var exportModule = host.currentProcess.Modules.getValueAt(mod);\n    var start = exportModule.BaseAddress;\n    var end = exportModule.BaseAddress.add(exportModule.Size);\n    return calls.Where(x => x.ReturnAddress.compareTo(start) < 0 ||\n                            x.ReturnAddress.compareTo(end) > 0);\n}\n"
  },
  {
    "path": "watchForStackCorruption.js",
    "content": "﻿\"use strict\";\n\n// Usage: Load this script by \".scriptload\" or with the WinDbg script window.\n//        Then run dx @$scriptContents.watchFunctionForStackCorruption(\"FuncName\")\n//        Continue execution and the debugger will break in when the specified function has its return\n//        address overwritten.\n// Note: This will script will not work if the watched function is called from more than 4 threads\n//       at the same time, since it uses a hardware breakpoint on each invocation of the watched func.\n\n\nfunction initializeScript()\n{\n    return [new host.apiVersionSupport(1, 7)];\n}\n\nfunction watchFunctionForStackCorruption(functionName)\n{\n    var bp = host.namespace.Debugger.Utility.Control.SetBreakpointAtOffset(functionName, 0);\n    bp.Condition = \"@$scriptContents.onWatchedFunctionEntry()\";\n}\n\nfunction onWatchedFunctionEntry()\n{\n    var stackPtr = host.namespace.Debugger.State.PseudoRegisters.General.csp;\n    var corruptionBP = host.namespace.Debugger.Utility.Control.SetBreakpointForReadWrite(stackPtr, \"w\", 8);\n    corruptionBP.Command = \".echo Stack corruption detected!\";\n    var returnAddr = host.namespace.Debugger.State.PseudoRegisters.General.ra;\n    // This is a bit ugly, but I don't think there's a way to set \"one shot\" breakpoints\n    // from Debugger.Utility.Control or the Breakpoint object.\n    var addrString = returnAddr.address.toString(16);\n    host.namespace.Debugger.Utility.Control.ExecuteCommand(\"bp /1 \" + addrString + '\"bc ' + corruptionBP.Id + '; g\"');\n    return false;\n}"
  }
]