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 |