1#!/usr/bin/env python
2# Copyright (c) 2011 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""This will retrieve a PDBs "fingerprint" from it's corresponding executable
7image (.dll or .exe).  This is used when retrieving the PDB from the symbol
8server.  The .pdb (or cab compressed .pd_) is expected at a path like:
9 foo.pdb/FINGERPRINT/foo.pdb
10
11We can retrieve the same information from the .PDB file itself, but this file
12format is much more difficult and undocumented.  Instead, we can look at the
13DLL's reference to the PDB, and use that to retrieve the information."""
14
15import sys
16import pefile
17
18
19__CV_INFO_PDB70_format__ = ('CV_INFO_PDB70',
20  ('4s,CvSignature', '16s,Signature', 'L,Age'))
21
22__GUID_format__ = ('GUID',
23  ('L,Data1', 'H,Data2', 'H,Data3', '8s,Data4'))
24
25
26def GetPDBInfoFromImg(filename):
27  """Returns the PDB fingerprint and the pdb filename given an image file"""
28
29  pe = pefile.PE(filename)
30
31  for dbg in pe.DIRECTORY_ENTRY_DEBUG:
32    if dbg.struct.Type == 2:  # IMAGE_DEBUG_TYPE_CODEVIEW
33      off = dbg.struct.AddressOfRawData
34      size = dbg.struct.SizeOfData
35      data = pe.get_memory_mapped_image()[off:off+size]
36
37      cv = pefile.Structure(__CV_INFO_PDB70_format__)
38      cv.__unpack__(data)
39      cv.PdbFileName = data[cv.sizeof():]
40      guid = pefile.Structure(__GUID_format__)
41      guid.__unpack__(cv.Signature)
42      guid.Data4_0 = ''.join("%02X" % ord(x) for x in guid.Data4[0:2])
43      guid.Data4_1 = ''.join("%02X" % ord(x) for x in guid.Data4[2:])
44
45      return ("%08X%04X%04X%s%s%d" % (
46          guid.Data1, guid.Data2, guid.Data3,
47          guid.Data4_0, guid.Data4_1, cv.Age),
48          cv.PdbFileName.split('\x00', 1)[0])
49
50    break
51
52
53def main():
54  if len(sys.argv) != 2:
55    print "usage: file.dll"
56    return 1
57
58  (fingerprint, filename) = GetPDBInfoFromImg(sys.argv[1])
59  print "%s %s" % (fingerprint, filename)
60  return 0
61
62
63if __name__ == '__main__':
64  sys.exit(main())
65