Home | History | Annotate | Download | only in checkbins
      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 """Makes sure that all EXE and DLL files in the provided directory were built
      7 correctly.
      8 
      9 In essense it runs a subset of BinScope tests ensuring that binaries have
     10 /NXCOMPAT, /DYNAMICBASE and /SAFESEH.
     11 """
     12 
     13 import os
     14 import optparse
     15 import sys
     16 
     17 # Find /third_party/pefile based on current directory and script path.
     18 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..',
     19                              'third_party', 'pefile'))
     20 import pefile
     21 
     22 PE_FILE_EXTENSIONS = ['.exe', '.dll']
     23 DYNAMICBASE_FLAG = 0x0040
     24 NXCOMPAT_FLAG = 0x0100
     25 NO_SEH_FLAG = 0x0400
     26 MACHINE_TYPE_AMD64 = 0x8664
     27 
     28 # Please do not add your file here without confirming that it indeed doesn't
     29 # require /NXCOMPAT and /DYNAMICBASE.  Contact cpu (at] chromium.org or your local
     30 # Windows guru for advice.
     31 EXCLUDED_FILES = ['chrome_frame_mini_installer.exe',
     32                   'mini_installer.exe',
     33                   'wow_helper.exe',
     34                   'xinput1_3.dll' # Microsoft DirectX redistributable.
     35                   ]
     36 
     37 def IsPEFile(path):
     38   return (os.path.isfile(path) and
     39           os.path.splitext(path)[1].lower() in PE_FILE_EXTENSIONS and
     40           os.path.basename(path) not in EXCLUDED_FILES)
     41 
     42 def main(options, args):
     43   directory = args[0]
     44   pe_total = 0
     45   pe_passed = 0
     46 
     47   for file in os.listdir(directory):
     48     path = os.path.abspath(os.path.join(directory, file))
     49     if not IsPEFile(path):
     50       continue
     51     pe = pefile.PE(path, fast_load=True)
     52     pe.parse_data_directories(directories=[
     53         pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']])
     54     pe_total = pe_total + 1
     55     success = True
     56 
     57     # Check for /DYNAMICBASE.
     58     if pe.OPTIONAL_HEADER.DllCharacteristics & DYNAMICBASE_FLAG:
     59       if options.verbose:
     60         print "Checking %s for /DYNAMICBASE... PASS" % path
     61     else:
     62       success = False
     63       print "Checking %s for /DYNAMICBASE... FAIL" % path
     64 
     65     # Check for /NXCOMPAT.
     66     if pe.OPTIONAL_HEADER.DllCharacteristics & NXCOMPAT_FLAG:
     67       if options.verbose:
     68         print "Checking %s for /NXCOMPAT... PASS" % path
     69     else:
     70       success = False
     71       print "Checking %s for /NXCOMPAT... FAIL" % path
     72 
     73     # Check for /SAFESEH. Binaries should meet one of the following
     74     # criteria:
     75     #   1) Have no SEH table as indicated by the DLL characteristics
     76     #   2) Have a LOAD_CONFIG section containing a valid SEH table
     77     #   3) Be a 64-bit binary, in which case /SAFESEH isn't required
     78     #
     79     # Refer to the following MSDN article for more information:
     80     # http://msdn.microsoft.com/en-us/library/9a89h429.aspx
     81     if (pe.OPTIONAL_HEADER.DllCharacteristics & NO_SEH_FLAG or
     82         (hasattr(pe, "DIRECTORY_ENTRY_LOAD_CONFIG") and
     83          pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SEHandlerCount > 0 and
     84          pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SEHandlerTable != 0) or
     85         pe.FILE_HEADER.Machine == MACHINE_TYPE_AMD64):
     86       if options.verbose:
     87         print "Checking %s for /SAFESEH... PASS" % path
     88     else:
     89       success = False
     90       print "Checking %s for /SAFESEH... FAIL" % path
     91 
     92     # ASLR is weakened on Windows 64-bit when the ImageBase is below 4GB
     93     # (because the loader will never be rebase the image above 4GB).
     94     if pe.FILE_HEADER.Machine == MACHINE_TYPE_AMD64:
     95       if pe.OPTIONAL_HEADER.ImageBase <= 0xFFFFFFFF:
     96         print("Checking %s ImageBase (0x%X < 4GB)... FAIL" %
     97               (path, pe.OPTIONAL_HEADER.ImageBase))
     98         success = False
     99       elif options.verbose:
    100         print("Checking %s ImageBase (0x%X > 4GB)... PASS" %
    101               (path, pe.OPTIONAL_HEADER.ImageBase))
    102 
    103     # Update tally.
    104     if success:
    105       pe_passed = pe_passed + 1
    106 
    107   print "Result: %d files found, %d files passed" % (pe_total, pe_passed)
    108   if pe_passed != pe_total:
    109     sys.exit(1)
    110 
    111 if __name__ == '__main__':
    112   usage = "Usage: %prog [options] DIRECTORY"
    113   option_parser = optparse.OptionParser(usage=usage)
    114   option_parser.add_option("-v", "--verbose", action="store_true",
    115                            default=False, help="Print debug logging")
    116   options, args = option_parser.parse_args()
    117   if not args:
    118     option_parser.print_help()
    119     sys.exit(0)
    120   main(options, args)
    121