1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2011 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""This will retrieve a PDBs "fingerprint" from it's corresponding executable
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)image (.dll or .exe).  This is used when retrieving the PDB from the symbol
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)server.  The .pdb (or cab compressed .pd_) is expected at a path like:
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) foo.pdb/FINGERPRINT/foo.pdb
10ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)We can retrieve the same information from the .PDB file itself, but this file
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)format is much more difficult and undocumented.  Instead, we can look at the
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DLL's reference to the PDB, and use that to retrieve the information."""
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import pefile
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)__CV_INFO_PDB70_format__ = ('CV_INFO_PDB70',
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ('4s,CvSignature', '16s,Signature', 'L,Age'))
21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)__GUID_format__ = ('GUID',
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ('L,Data1', 'H,Data2', 'H,Data3', '8s,Data4'))
24d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
2568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
2668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)def GetPDBInfoFromImg(filename):
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Returns the PDB fingerprint and the pdb filename given an image file"""
28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  pe = pefile.PE(filename)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for dbg in pe.DIRECTORY_ENTRY_DEBUG:
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if dbg.struct.Type == 2:  # IMAGE_DEBUG_TYPE_CODEVIEW
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      off = dbg.struct.AddressOfRawData
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      size = dbg.struct.SizeOfData
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data = pe.get_memory_mapped_image()[off:off+size]
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cv = pefile.Structure(__CV_INFO_PDB70_format__)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cv.__unpack__(data)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cv.PdbFileName = data[cv.sizeof():]
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      guid = pefile.Structure(__GUID_format__)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      guid.__unpack__(cv.Signature)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      guid.Data4_0 = ''.join("%02X" % ord(x) for x in guid.Data4[0:2])
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      guid.Data4_1 = ''.join("%02X" % ord(x) for x in guid.Data4[2:])
448bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ("%08X%04X%04X%s%s%d" % (
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          guid.Data1, guid.Data2, guid.Data3,
47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          guid.Data4_0, guid.Data4_1, cv.Age),
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          cv.PdbFileName.split('\x00', 1)[0])
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    break
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def main():
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if len(sys.argv) != 2:
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    print "usage: file.dll"
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 1
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (fingerprint, filename) = GetPDBInfoFromImg(sys.argv[1])
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print "%s %s" % (fingerprint, filename)
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return 0
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)if __name__ == '__main__':
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sys.exit(main())
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)