#!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()