#!/usr/bin/env python # Copyright (c) 2011 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Makes sure that all EXE and DLL files in the provided directory were built correctly. In essense it runs a subset of BinScope tests ensuring that binaries have /NXCOMPAT, /DYNAMICBASE and /SAFESEH. """ import os import optparse import sys # Find /third_party/pefile based on current directory and script path. sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'third_party', 'pefile')) import pefile PE_FILE_EXTENSIONS = ['.exe', '.dll'] DYNAMICBASE_FLAG = 0x0040 NXCOMPAT_FLAG = 0x0100 NO_SEH_FLAG = 0x0400 MACHINE_TYPE_AMD64 = 0x8664 # Please do not add your file here without confirming that it indeed doesn't # require /NXCOMPAT and /DYNAMICBASE. Contact cpu@chromium.org or your local # Windows guru for advice. EXCLUDED_FILES = ['chrome_frame_mini_installer.exe', 'mini_installer.exe', 'wow_helper.exe', 'xinput1_3.dll' # Microsoft DirectX redistributable. ] def IsPEFile(path): return (os.path.isfile(path) and os.path.splitext(path)[1].lower() in PE_FILE_EXTENSIONS and os.path.basename(path) not in EXCLUDED_FILES) def main(options, args): directory = args[0] pe_total = 0 pe_passed = 0 for file in os.listdir(directory): path = os.path.abspath(os.path.join(directory, file)) if not IsPEFile(path): continue pe = pefile.PE(path, fast_load=True) pe.parse_data_directories(directories=[ pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']]) pe_total = pe_total + 1 success = True # Check for /DYNAMICBASE. if pe.OPTIONAL_HEADER.DllCharacteristics & DYNAMICBASE_FLAG: if options.verbose: print "Checking %s for /DYNAMICBASE... PASS" % path else: success = False print "Checking %s for /DYNAMICBASE... FAIL" % path # Check for /NXCOMPAT. if pe.OPTIONAL_HEADER.DllCharacteristics & NXCOMPAT_FLAG: if options.verbose: print "Checking %s for /NXCOMPAT... PASS" % path else: success = False print "Checking %s for /NXCOMPAT... FAIL" % path # Check for /SAFESEH. Binaries should meet one of the following # criteria: # 1) Have no SEH table as indicated by the DLL characteristics # 2) Have a LOAD_CONFIG section containing a valid SEH table # 3) Be a 64-bit binary, in which case /SAFESEH isn't required # # Refer to the following MSDN article for more information: # http://msdn.microsoft.com/en-us/library/9a89h429.aspx if (pe.OPTIONAL_HEADER.DllCharacteristics & NO_SEH_FLAG or (hasattr(pe, "DIRECTORY_ENTRY_LOAD_CONFIG") and pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SEHandlerCount > 0 and pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SEHandlerTable != 0) or pe.FILE_HEADER.Machine == MACHINE_TYPE_AMD64): if options.verbose: print "Checking %s for /SAFESEH... PASS" % path else: success = False print "Checking %s for /SAFESEH... FAIL" % path # Update tally. if success: pe_passed = pe_passed + 1 print "Result: %d files found, %d files passed" % (pe_total, pe_passed) if pe_passed != pe_total: sys.exit(1) if __name__ == '__main__': usage = "Usage: %prog [options] DIRECTORY" option_parser = optparse.OptionParser(usage=usage) option_parser.add_option("-v", "--verbose", action="store_true", default=False, help="Print debug logging") options, args = option_parser.parse_args() if not args: option_parser.print_help() sys.exit(0) main(options, args)