Skip to content

Instantly share code, notes, and snippets.

@marshal20
Created February 11, 2025 12:28
Show Gist options
  • Select an option

  • Save marshal20/46977fe656ae2a8312c8e1c0352988e5 to your computer and use it in GitHub Desktop.

Select an option

Save marshal20/46977fe656ae2a8312c8e1c0352988e5 to your computer and use it in GitHub Desktop.
Command line tool to get program database (PDB) debug symbols file URL from windows server. With options to download the PDB file directly.
#!python3
"""
Command line tool to get program database (PDB) debug symbols file URL from windows server.
With options to download the PDB file directly.
Credits to **Barakat@github** for the original script, though it didn't work without modifications:
https://gist.github.com/Barakat/e20ec993748c6ab382d727f08eaa523e
I fixed the GUID calculation which were ignoring Signature_Data5 and Signature_Data6 thus not creating a complete GUID.
Also I added the ability to download the file directly and a simple command line functionality.
example:
```console
$ python pdburl.py -d kernel32.dll
PDB symbols file for kernel32.dll:
https://msdl.microsoft.com/download/symbols/kernel32.pdb/5A77DE8CE8D58731F0EA38F1C92F48D81/kernel32.pdb
Downloading...
Downloaded: https://msdl.microsoft.com/download/symbols/kernel32.pdb/5A77DE8CE8D58731F0EA38F1C92F48D81/kernel32.pdb
To: kernel32.pdb
```
"""
import os
import argparse
import pefile
import urllib.request
SYMBOLS_SERVER = 'https://msdl.microsoft.com/download/symbols'
def get_pdb_symbol_url(binary_file_path: str):
"""
Gets the PDB debug symbols file of the given binary file from windows server.
Arguments:
binary_file_path: Binary file to get PDB to.
"""
pe = pefile.PE(binary_file_path, fast_load=True)
pe.parse_data_directories()
for directory in pe.DIRECTORY_ENTRY_DEBUG:
debug_entry = directory.entry
if hasattr(debug_entry, 'PdbFileName'):
pdb_file = debug_entry.PdbFileName[:-1].decode('ascii')
guid = ''
guid += f'{debug_entry.Signature_Data1:08x}'
guid += f'{debug_entry.Signature_Data2:04x}'
guid += f'{debug_entry.Signature_Data3:04x}'
guid += f'{debug_entry.Signature_Data4:02x}'
guid += f'{debug_entry.Signature_Data5:02x}'
guid += f'{int.from_bytes(debug_entry.Signature_Data6, byteorder='big'):012x}'
guid = guid.upper()
url = f'{SYMBOLS_SERVER}/{pdb_file}/{guid}{debug_entry.Age:x}/{pdb_file}'
return url
return None
def downlod_pdb_symbol_file(binary_file_path: str):
"""
Downloads the PDB debug symbols file of the given binary file from windows server.
Arguments:
binary_file_path: Binary file to get PDB to.
"""
# get url
url = get_pdb_symbol_url(binary_file_path)
print(f"PDB symbols file for {os.path.split(binary_file_path)[1]}:")
print(f"\t{url}")
# calculate output file path
output_dir = os.path.split(binary_file_path)[0]
output_file_name = os.path.split(url)[1]
output_file_path = os.path.join(output_dir, output_file_name)
# download
print(f"Downloading...")
urllib.request.urlretrieve(url, output_file_path)
print(f"\tDownloaded: {url}")
print(f"\tTo: {output_file_path}")
def main():
"""
Gets debug symbols url of a windows binary.
"""
# parse arguments
parser = argparse.ArgumentParser(description='Command line tool to get PDB symbols file URL from windows server for a windows binary file with the ability to download it.')
parser.add_argument('input', type=str, help='Input binary file.')
parser.add_argument('-d', "--download", action='store_true', help='Download the PDB symbols file instead of just outputing the URL.')
args = parser.parse_args()
# handle based on the download switch
if args.download:
downlod_pdb_symbol_file(args.input)
else:
# just print the URL
url = get_pdb_symbol_url(args.input)
print(f"PDB symbols file for {os.path.split(args.input)[1]}:")
print(f"\t{url}")
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment