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