15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2011 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Usage: change_mach_o_flags.py [--executable-heap] [--no-pie] <executablepath>
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Arranges for the executable at |executable_path| to have its data (heap)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pages protected to prevent execution on Mac OS X 10.7 ("Lion"), and to have
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)the PIE (position independent executable) bit set to enable ASLR (address
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)space layout randomization). With --executable-heap or --no-pie, the
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)respective bits are cleared instead of set, making the heap executable or
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)disabling PIE/ASLR.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)This script is able to operate on thin (single-architecture) Mach-O files
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)and fat (universal, multi-architecture) files. When operating on fat files,
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)it will set or clear the bits for each architecture contained therein.
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NON-EXECUTABLE HEAP
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Traditionally in Mac OS X, 32-bit processes did not have data pages set to
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)prohibit execution. Although user programs could call mprotect and
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)mach_vm_protect to deny execution of code in data pages, the kernel would
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)silently ignore such requests without updating the page tables, and the
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)hardware would happily execute code on such pages. 64-bit processes were
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)always given proper hardware protection of data pages. This behavior was
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)controllable on a system-wide level via the vm.allow_data_exec sysctl, which
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)is set by default to 1. The bit with value 1 (set by default) allows code
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)execution on data pages for 32-bit processes, and the bit with value 2
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)(clear by default) does the same for 64-bit processes.
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)In Mac OS X 10.7, executables can "opt in" to having hardware protection
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)against code execution on data pages applied. This is done by setting a new
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bit in the |flags| field of an executable's |mach_header|. When
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MH_NO_HEAP_EXECUTION is set, proper protections will be applied, regardless
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)of the setting of vm.allow_data_exec. See xnu-1699.22.73/osfmk/vm/vm_map.c
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)override_nx and xnu-1699.22.73/bsd/kern/mach_loader.c load_machfile.
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)The Apple toolchain has been revised to set the MH_NO_HEAP_EXECUTION when
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)producing executables, provided that -allow_heap_execute is not specified
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)at link time. Only linkers shipping with Xcode 4.0 and later (ld64-123.2 and
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)later) have this ability. See ld64-123.2.1/src/ld/Options.cpp
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Options::reconfigureDefaults() and
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ld64-123.2.1/src/ld/HeaderAndLoadCommands.hpp
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HeaderAndLoadCommandsAtom<A>::flags().
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)This script sets the MH_NO_HEAP_EXECUTION bit on Mach-O executables. It is
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)intended for use with executables produced by a linker that predates Apple's
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)modifications to set this bit itself. It is also useful for setting this bit
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)for non-i386 executables, including x86_64 executables. Apple's linker only
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sets it for 32-bit i386 executables, presumably under the assumption that
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)the value of vm.allow_data_exec is set in stone. However, if someone were to
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)change vm.allow_data_exec to 2 or 3, 64-bit x86_64 executables would run
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)without hardware protection against code execution on data pages. This
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)script can set the bit for x86_64 executables, guaranteeing that they run
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)with appropriate protection even when vm.allow_data_exec has been tampered
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)with.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)POSITION-INDEPENDENT EXECUTABLES/ADDRESS SPACE LAYOUT RANDOMIZATION
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)This script sets or clears the MH_PIE bit in an executable's Mach-O header,
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enabling or disabling position independence on Mac OS X 10.5 and later.
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Processes running position-independent executables have varying levels of
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ASLR protection depending on the OS release. The main executable's load
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)address, shared library load addresess, and the heap and stack base
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)addresses may be randomized. Position-independent executables are produced
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)by supplying the -pie flag to the linker (or defeated by supplying -no_pie).
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Executables linked with a deployment target of 10.7 or higher have PIE on
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)by default.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)This script is never strictly needed during the build to enable PIE, as all
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)linkers used are recent enough to support -pie. However, it's used to
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)disable the PIE bit as needed on already-linked executables.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import optparse
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import struct
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# <mach-o/fat.h>
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FAT_MAGIC = 0xcafebabe
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FAT_CIGAM = 0xbebafeca
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# <mach-o/loader.h>
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MH_MAGIC = 0xfeedface
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MH_CIGAM = 0xcefaedfe
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MH_MAGIC_64 = 0xfeedfacf
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MH_CIGAM_64 = 0xcffaedfe
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MH_EXECUTE = 0x2
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MH_PIE = 0x00200000
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MH_NO_HEAP_EXECUTION = 0x01000000
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MachOError(Exception):
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A class for exceptions thrown by this module."""
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pass
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CheckedSeek(file, offset):
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Seeks the file-like object at |file| to offset |offset| and raises a
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MachOError if anything funny happens."""
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file.seek(offset, os.SEEK_SET)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_offset = file.tell()
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if new_offset != offset:
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise MachOError, \
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'seek: expected offset %d, observed %d' % (offset, new_offset)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CheckedRead(file, count):
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Reads |count| bytes from the file-like |file| object, raising a
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MachOError if any other number of bytes is read."""
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bytes = file.read(count)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(bytes) != count:
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise MachOError, \
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'read: expected length %d, observed %d' % (count, len(bytes))
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return bytes
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ReadUInt32(file, endian):
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Reads an unsinged 32-bit integer from the file-like |file| object,
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  treating it as having endianness specified by |endian| (per the |struct|
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  module), and returns it as a number. Raises a MachOError if the proper
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  length of data can't be read from |file|."""
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bytes = CheckedRead(file, 4)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (uint32,) = struct.unpack(endian + 'I', bytes)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return uint32
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ReadMachHeader(file, endian):
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Reads an entire |mach_header| structure (<mach-o/loader.h>) from the
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file-like |file| object, treating it as having endianness specified by
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  |endian| (per the |struct| module), and returns a 7-tuple of its members
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  as numbers. Raises a MachOError if the proper length of data can't be read
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  from |file|."""
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bytes = CheckedRead(file, 28)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      struct.unpack(endian + '7I', bytes)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ReadFatArch(file):
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Reads an entire |fat_arch| structure (<mach-o/fat.h>) from the file-like
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  |file| object, treating it as having endianness specified by |endian|
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (per the |struct| module), and returns a 5-tuple of its members as numbers.
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Raises a MachOError if the proper length of data can't be read from
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  |file|."""
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bytes = CheckedRead(file, 20)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cputype, cpusubtype, offset, size, align = struct.unpack('>5I', bytes)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return cputype, cpusubtype, offset, size, align
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def WriteUInt32(file, uint32, endian):
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Writes |uint32| as an unsinged 32-bit integer to the file-like |file|
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  object, treating it as having endianness specified by |endian| (per the
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  |struct| module)."""
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bytes = struct.pack(endian + 'I', uint32)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert len(bytes) == 4
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file.write(bytes)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def HandleMachOFile(file, options, offset=0):
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Seeks the file-like |file| object to |offset|, reads its |mach_header|,
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  and rewrites the header's |flags| field if appropriate. The header's
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  endianness is detected. Both 32-bit and 64-bit Mach-O headers are supported
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (mach_header and mach_header_64). Raises MachOError if used on a header that
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  does not have a known magic number or is not of type MH_EXECUTE. The
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MH_PIE and MH_NO_HEAP_EXECUTION bits are set or cleared in the |flags| field
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  according to |options| and written to |file| if any changes need to be made.
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  If already set or clear as specified by |options|, nothing is written."""
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CheckedSeek(file, offset)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  magic = ReadUInt32(file, '<')
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if magic == MH_MAGIC or magic == MH_MAGIC_64:
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    endian = '<'
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif magic == MH_CIGAM or magic == MH_CIGAM_64:
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    endian = '>'
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise MachOError, \
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'Mach-O file at offset %d has illusion of magic' % offset
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CheckedSeek(file, offset)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ReadMachHeader(file, endian)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert magic == MH_MAGIC or magic == MH_MAGIC_64
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if filetype != MH_EXECUTE:
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise MachOError, \
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'Mach-O file at offset %d is type 0x%x, expected MH_EXECUTE' % \
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              (offset, filetype)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  original_flags = flags
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if options.no_heap_execution:
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    flags |= MH_NO_HEAP_EXECUTION
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    flags &= ~MH_NO_HEAP_EXECUTION
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if options.pie:
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    flags |= MH_PIE
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    flags &= ~MH_PIE
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if flags != original_flags:
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CheckedSeek(file, offset + 24)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WriteUInt32(file, flags, endian)
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def HandleFatFile(file, options, fat_offset=0):
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Seeks the file-like |file| object to |offset| and loops over its
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  |fat_header| entries, calling HandleMachOFile for each."""
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CheckedSeek(file, fat_offset)
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  magic = ReadUInt32(file, '>')
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert magic == FAT_MAGIC
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  nfat_arch = ReadUInt32(file, '>')
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for index in xrange(0, nfat_arch):
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cputype, cpusubtype, offset, size, align = ReadFatArch(file)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert size >= 28
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # HandleMachOFile will seek around. Come back here after calling it, in
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # case it sought.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fat_arch_offset = file.tell()
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleMachOFile(file, options, offset)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CheckedSeek(file, fat_arch_offset)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main(me, args):
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser = optparse.OptionParser('%prog [options] <executable_path>')
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option('--executable-heap', action='store_false',
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    dest='no_heap_execution', default=True,
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    help='Clear the MH_NO_HEAP_EXECUTION bit')
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option('--no-pie', action='store_false',
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    dest='pie', default=True,
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    help='Clear the MH_PIE bit')
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (options, loose_args) = parser.parse_args(args)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(loose_args) != 1:
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parser.print_usage()
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  executable_path = loose_args[0]
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  executable_file = open(executable_path, 'rb+')
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  magic = ReadUInt32(executable_file, '<')
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if magic == FAT_CIGAM:
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Check FAT_CIGAM and not FAT_MAGIC because the read was little-endian.
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleFatFile(executable_file, options)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif magic == MH_MAGIC or magic == MH_CIGAM or \
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      magic == MH_MAGIC_64 or magic == MH_CIGAM_64:
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleMachOFile(executable_file, options)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise MachOError, '%s is not a Mach-O or fat file' % executable_file
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  executable_file.close()
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__':
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sys.exit(main(sys.argv[0], sys.argv[1:]))
274