| Index: build/mac/change_mach_o_flags.py
|
| ===================================================================
|
| --- build/mac/change_mach_o_flags.py (revision 97870)
|
| +++ build/mac/change_mach_o_flags.py (working copy)
|
| @@ -4,11 +4,21 @@
|
| # Use of this source code is governed by a BSD-style license that can be
|
| # found in the LICENSE file.
|
|
|
| -# Usage: make_heap_non_executable.py <executable_path>
|
| +# Usage: change_mach_o_flags.py [--executable-heap] [--no-pie] <executable_path>
|
| #
|
| # Arranges for the executable at |executable_path| to have its data (heap)
|
| -# pages protected to prevent execution on Mac OS X 10.7 ("Lion").
|
| +# pages protected to prevent execution on Mac OS X 10.7 ("Lion"), and to have
|
| +# the PIE (position independent executable) bit set to enable ASLR (address
|
| +# space layout randomization). With --executable-heap or --no-pie, the
|
| +# respective bits are cleared instead of set, making the heap executable or
|
| +# disabling PIE/ASLR.
|
| #
|
| +# This script is able to operate on thin (single-architecture) Mach-O files
|
| +# and fat (universal, multi-architecture) files. When operating on fat files,
|
| +# it will set or clear the bits for each architecture contained therein.
|
| +#
|
| +# NON-EXECUTABLE HEAP
|
| +#
|
| # Traditionally in Mac OS X, 32-bit processes did not have data pages set to
|
| # prohibit execution. Although user programs could call mprotect and
|
| # mach_vm_protect to deny execution of code in data pages, the kernel would
|
| @@ -47,12 +57,24 @@
|
| # with appropriate protection even when vm.allow_data_exec has been tampered
|
| # with.
|
| #
|
| -# This script is able to operate on thin (single-architecture) Mach-O files
|
| -# and fat (universal, multi-architecture) files. When operating on fat files,
|
| -# it will set the MH_NO_HEAP_EXECUTION bit for each architecture contained
|
| -# therein.
|
| +# POSITION-INDEPENDENT EXECUTABLES/ADDRESS SPACE LAYOUT RANDOMIZATION
|
| +#
|
| +# This script sets or clears the MH_PIE bit in an executable's Mach-O header,
|
| +# enabling or disabling position independence on Mac OS X 10.5 and later.
|
| +# Processes running position-independent executables have varying levels of
|
| +# ASLR protection depending on the OS release. The main executable's load
|
| +# address, shared library load addresess, and the heap and stack base
|
| +# addresses may be randomized. Position-independent executables are produced
|
| +# by supplying the -pie flag to the linker (or defeated by supplying -no_pie).
|
| +# Executables linked with a deployment target of 10.7 or higher have PIE on
|
| +# by default.
|
| +#
|
| +# This script is never strictly needed during the build to enable PIE, as all
|
| +# linkers used are recent enough to support -pie. However, it's used to
|
| +# disable the PIE bit as needed on already-linked executables.
|
|
|
|
|
| +import optparse
|
| import os
|
| import struct
|
| import sys
|
| @@ -68,7 +90,8 @@
|
| MH_MAGIC_64 = 0xfeedfacf
|
| MH_CIGAM_64 = 0xcffaedfe
|
| MH_EXECUTE = 0x2
|
| -MH_NO_HEAP_EXECUTION = 0x1000000
|
| +MH_PIE = 0x00200000
|
| +MH_NO_HEAP_EXECUTION = 0x01000000
|
|
|
|
|
| class MachOError(Exception):
|
| @@ -150,14 +173,15 @@
|
| file.write(bytes)
|
|
|
|
|
| -def HandleMachOFile(file, offset=0):
|
| +def HandleMachOFile(file, options, offset=0):
|
| """Seeks the file-like |file| object to |offset|, reads its |mach_header|,
|
| and rewrites the header's |flags| field if appropriate. The header's
|
| endianness is detected. Both 32-bit and 64-bit Mach-O headers are supported
|
| (mach_header and mach_header_64). Raises MachOError if used on a header that
|
| does not have a known magic number or is not of type MH_EXECUTE. The
|
| - MH_NO_HEAP_EXECUTION is set in the |flags| field and written to |file| if
|
| - not already set. If already set, nothing is written."""
|
| + MH_PIE and MH_NO_HEAP_EXECUTION bits are set or cleared in the |flags| field
|
| + according to |options| and written to |file| if any changes need to be made.
|
| + If already set or clear as specified by |options|, nothing is written."""
|
|
|
| CheckedSeek(file, offset)
|
| magic = ReadUInt32(file, '<')
|
| @@ -178,13 +202,24 @@
|
| 'Mach-O file at offset %d is type 0x%x, expected MH_EXECUTE' % \
|
| (offset, filetype)
|
|
|
| - if not flags & MH_NO_HEAP_EXECUTION:
|
| + original_flags = flags
|
| +
|
| + if options.no_heap_execution:
|
| flags |= MH_NO_HEAP_EXECUTION
|
| + else:
|
| + flags &= ~MH_NO_HEAP_EXECUTION
|
| +
|
| + if options.pie:
|
| + flags |= MH_PIE
|
| + else:
|
| + flags &= ~MH_PIE
|
| +
|
| + if flags != original_flags:
|
| CheckedSeek(file, offset + 24)
|
| WriteUInt32(file, flags, endian)
|
|
|
|
|
| -def HandleFatFile(file, fat_offset=0):
|
| +def HandleFatFile(file, options, fat_offset=0):
|
| """Seeks the file-like |file| object to |offset| and loops over its
|
| |fat_header| entries, calling HandleMachOFile for each."""
|
|
|
| @@ -201,25 +236,33 @@
|
| # HandleMachOFile will seek around. Come back here after calling it, in
|
| # case it sought.
|
| fat_arch_offset = file.tell()
|
| - HandleMachOFile(file, offset)
|
| + HandleMachOFile(file, options, offset)
|
| CheckedSeek(file, fat_arch_offset)
|
|
|
|
|
| def main(me, args):
|
| - if len(args) != 1:
|
| - print >>sys.stderr, 'usage: %s <executable_path>' % me
|
| + parser = optparse.OptionParser('%prog [options] <executable_path>')
|
| + parser.add_option('--executable-heap', action='store_false',
|
| + dest='no_heap_execution', default=True,
|
| + help='Clear the MH_NO_HEAP_EXECUTION bit')
|
| + parser.add_option('--no-pie', action='store_false',
|
| + dest='pie', default=True,
|
| + help='Clear the MH_PIE bit')
|
| + (options, loose_args) = parser.parse_args(args)
|
| + if len(loose_args) != 1:
|
| + parser.print_usage()
|
| return 1
|
|
|
| - executable_path = args[0]
|
| + executable_path = loose_args[0]
|
| executable_file = open(executable_path, 'rb+')
|
|
|
| magic = ReadUInt32(executable_file, '<')
|
| if magic == FAT_CIGAM:
|
| # Check FAT_CIGAM and not FAT_MAGIC because the read was little-endian.
|
| - HandleFatFile(executable_file)
|
| + HandleFatFile(executable_file, options)
|
| elif magic == MH_MAGIC or magic == MH_CIGAM or \
|
| magic == MH_MAGIC_64 or magic == MH_CIGAM_64:
|
| - HandleMachOFile(executable_file)
|
| + HandleMachOFile(executable_file, options)
|
| else:
|
| raise MachOError, '%s is not a Mach-O or fat file' % executable_file
|
|
|
|
|