[PowerShell Script] Downloading PDB for Specific Modules

A few weeks ago, during a laboratory with a customer, I found myself struggling to download the public symbol from a specific driver. Since driver is Kernel Mode if you get a User Mode dump from the application using the driver, you won’t be able to actually see and download the driver. If you have a Kernel dump, you can get the module and download the symbols, but it’s more work.

There are other approaches and tools, but during this lab a colleague from PFE recommended symchk.exe, which is part of Debugging Tools for Windows. It turned out to be the best approach for my needs; however, chances are I’ll forget the syntax and my customer, too. To avoid this problem, I decided to create a small script that acts as a wrapper for symchk. To use it you have to pass 4 required parameters:

<Folder that Has the Modules> - Folder that has the modules (dlls/exes)

<Module Name or Mask> - You can use something like *.dll or the module name, like tcpip.dll.

<Folder To Save the Symbols>- Name for the folder that is going to store the symbols.

<Folder Where Debugging Tools For Windows is Installed>- Directory where Debugging tools for Windows was installed.

Usage:

Let’s suppose my dlls are in c:\test\dlls, my debugger is in c:\debuggers, and I want to save the public symbols for all dlls ( *.dll) in c:\test\symbols.

Here is the command line:

.\GetPDBForModules "c:\test\dlls" " *.dll" "c:\test\symbols" "c:\debuggers"

Before - just our binaries:

Execution:

 

After execution - binaries, symbols and manifest file:

 

Source code for GetPDBForModules.PS1:

########################################################################################################

# Script: GetPDBforModules.ps1

#

# Parameters: <dirModules> - Folder that has the modules (dlls)

# <moduleName> - You can use something like *.dll or the module name.

# <outputDirName> - Name for the folder that is going to store the symbols.

# <dirForDbgTools> - Directory where Debugging tools for Windows was installed.

#

# Purpose: Access the Microsoft public symbols and download the symbols for each module.

#

# Requirement: Debugging Tools For Windows.

#

# Usage: .\GetPDBforModules "c:\modules" "ntdll.dll" "c:\symbols" "c:\debuggers"

 # .\GetPDBforModules "c:\modules" "*.dll" "c:\symbols" "c:\debuggers"

#

# Changes History:

#

# Roberto Alexis Farah

# All my functions are provided "AS IS" with no warranties, and confer no rights.

########################################################################################################

param(

      [string] $dirModules     = $(throw "Error! You must provide the path which has the modules like: c:\modules"),

      [string] $moduleName     = $(throw "Error! You must provide the file name like: ntdll.dll or *.dll or *.exe"),

      [string] $outputDirName  = $(throw "Error! You must provide the path which is going to store the symbols, like: c:\symbols"),

      [string] $dirForDbgTools = $(throw "Error! You must provide the path for Debugging Toos For Windows.")

     )

set-psdebug -strict

$ErrorActionPreference = "stop"

trap {"Error message: $_"}

$tempFile = "Manifest.txt" # We use $outputDirName plus .txt to create the manifest text file.

$dirForDbgTools += "\symchk"   # Add symchk.exe to path.

# In case we are adding one more \, let's remove the extra \.

$dirForDbgTools = $dirForDbgTools.Replace("\\", "\")

# Windows Shell to call the console application.

$instance = new-object -comobject WScript.Shell

write-Host "`nExtracting manifest file from module(s)...`n" -foreground Green -background Black

$temp = "$dirModules\$moduleName".Replace("\\", "\")

# Creates directory. If it already exists it's ok.

$hideOutput = md "$outputDirName"

# It should be something like: symchk /om C:\SymListManifest /if c:\WINDOWS\system32\*.dll

$argument = "$dirForDbgTools /om $outputDirName$tempFile /if $temp"

write-Host  $argument -foreground Red -background Black

$output = $instance.Run($argument, 3)

# The Run() method from WshShell doesn't wait for the callee to finish the execution, so let's wait for 10 seconds.

# On next version let's monitor if the console window had disappeared, so we don't have to use the forced delay.

start-Sleep 10

write-Host "`nDone!" -foreground Green -background Black

write-Host "`nDownloading symbols to $outputDirName folder..." -foreground Green -background Black

$argument = "$dirForDbgTools /im $outputDirName$tempFile /s SRV*$outputDirName*https://msdl.microsoft.com/download/symbols"

write-Host  $argument -foreground Red -background Black

$output = $instance.Run($argument, 3)

write-Host "`nDone! "  -foreground Green -background Black -nonewline

write-Host $outputDirName  -foreground Red -background Black -nonewline

write-Host " has all requested PDB files.`n"  -foreground Green -background Black