Ejemplo n.º 1
0
    def fuzz(self, filename, output_filename):
        self.macho = MachO(filename)

        changes = random.randint(1, 25)
        for i in range(changes * 5):
            self.do_fuzz_internal()
            if len(self.change_list) == changes:
                break

        # Copy the contents of the original file to the output file
        f = open(output_filename, "wb+")
        f.write(open(filename, "rb").read())
        f.close()

        # Update it's contents
        f = open(output_filename, "rb+")
        self.macho.write(f)
        f.close()

        # And write the .diff file
        f = open(output_filename + ".diff", "wb")
        f.write("# Original file created by 'MachO Mutator' was %s\n" %
                filename)
        for change in self.changes:
            print "# CHANGE: %s" % ", ".join(change)
            f.write("# CHANGE: %s\n" % ", ".join(change))
        f.close()

        os.system("radiff2 %s %s" % (filename, output_filename))
Ejemplo n.º 2
0
 def _get_machine_type(self, path):
     try:
         pe = pefile.PE(path)
         format_ = 'PE'
         if pefile.MACHINE_TYPE[pe.FILE_HEADER.Machine].find('I386') != -1:
             arch = '32-bit'
         else:
             arch = '64-bit'
     except pefile.PEFormatError, detail:
         try:
             self._dprint(detail)
             m = MachO(path)
             format_ = 'Mach-O'
             for header in m.headers:
                 if CPU_TYPE_NAMES.get(header.header.cputype,
                                       header.header.cputype) == 'x86_64':
                     #if header.MH_MAGIC == MH_MAGIC_64:
                     arch = '64-bit'
                 else:
                     arch = '32-bit'
         except:
             try:
                 elffile = ELFFile(open(path, 'rb'))
                 format_ = 'ELF'
                 e_ident = elffile.header['e_ident']
                 if e_ident['EI_CLASS'] == 'ELFCLASS64':
                     arch = '64-bit'
                 else:
                     arch = '32-bit'
             except:
                 return None, None
Ejemplo n.º 3
0
def extract_shellcode(filename):
    # find offset of _text and _data and extract to bin file
    b = os.path.splitext(filename)[0]
    macho_filename = os.path.join(SRC_DIR, "%s.macho" % (b))
    fileoffset = 0
    shellcodesize = 0
    m = MachO(macho_filename)
    for (load_cmd, cmd, data) in m.headers[0].commands:
        if data:
            if hasattr(data[0], "sectname"):
                sectionName = getattr(data[0], 'sectname', '').rstrip('\0')
                if "text" in sectionName:
                    fileoffset = data[0].offset
                    shellcodesize += data[0].size
                if "data" in sectionName:
                    shellcodesize += data[0].size
    shellcode_filename = os.path.join(SRC_DIR, "%s_shellcode.bin" % (b))
    with open(macho_filename, 'rb') as f:
        f.seek(fileoffset, 1)
        shellcode_bytes = f.read(shellcodesize)
        with open(shellcode_filename, 'wb') as sf:
            sf.write(shellcode_bytes)
            sf.close()
        f.close()
    return shellcode_bytes
Ejemplo n.º 4
0
def macholib_get_paths(cur_path):
    """
    Get rpaths, dependencies and id of mach-o objects
    using python macholib package
    """
    dll = MachO(cur_path)

    ident = None
    rpaths = list()
    deps = list()
    for header in dll.headers:
        rpaths = [
            data.rstrip(b'\0').decode('utf-8')
            for load_command, dylib_command, data in header.commands
            if load_command.cmd == macholib.mach_o.LC_RPATH
        ]
        deps = [
            data.rstrip(b'\0').decode('utf-8')
            for load_command, dylib_command, data in header.commands
            if load_command.cmd == macholib.mach_o.LC_LOAD_DYLIB
        ]
        idents = [
            data.rstrip(b'\0').decode('utf-8')
            for load_command, dylib_command, data in header.commands
            if load_command.cmd == macholib.mach_o.LC_ID_DYLIB
        ]
        if len(idents) == 1:
            ident = idents[0]
    tty.debug('ident: %s' % ident)
    tty.debug('deps: %s' % deps)
    tty.debug('rpaths: %s' % rpaths)
    return (rpaths, deps, ident)
Ejemplo n.º 5
0
  def fuzz(self, filename, output_filename):
    self.macho = MachO(filename)

    changes = random.randint(1, 25)
    for i in range(changes*5):
      self.do_fuzz_internal()
      if len(self.change_list) == changes:
        break

    # Copy the contents of the original file to the output file
    f = open(output_filename, "wb+")
    f.write(open(filename, "rb").read())
    f.close()

    # Update it's contents
    f = open(output_filename, "rb+")
    self.macho.write(f)
    f.close()

    # And write the .diff file
    f = open(output_filename + ".diff", "wb")
    f.write("# Original file created by 'MachO Mutator' was %s\n" % filename)
    for change in self.changes:
      print "# CHANGE: %s" % ", ".join(change)
      f.write("# CHANGE: %s\n" % ", ".join(change))
    f.close()
    
    os.system("radiff2 %s %s" % (filename, output_filename))
Ejemplo n.º 6
0
    def __init__(self, file_path):
        super(MachOExecutable, self).__init__(file_path)

        self.helper = MachO(self.fp)

        if self.helper.fat:
            raise Exception('MachO fat binaries are not supported at this time')

        self.architecture = self._identify_arch()

        if self.architecture is None:
            raise Exception('Architecture is not recognized')

        logging.debug('Initialized {} {} with file \'{}\''.format(self.architecture, type(self).__name__, file_path))

        self.pack_endianness = self.helper.headers[0].endian

        self.sections = []
        for lc, cmd, data in self.helper.headers[0].commands:
            if lc.cmd in (LC_SEGMENT, LC_SEGMENT_64):
                for section in data:
                    self.sections.append(section_from_macho_section(section, cmd))

        self.executable_segment = [cmd for lc, cmd, _ in self.helper.headers[0].commands
                                   if lc.cmd in (LC_SEGMENT, LC_SEGMENT_64) and cmd.initprot & 0x4][0]

        self.libraries = [fp.rstrip('\x00') for lc, cmd, fp in self.helper.headers[0].commands if lc.cmd == LC_LOAD_DYLIB]
Ejemplo n.º 7
0
def rewriteFramework(framework, frameworkMap):

    basename = os.path.splitext(os.path.basename(framework))[0]
    dyld = os.path.abspath(os.path.join(framework, basename))

    macho = MachO(dyld)

    def changefunc(key):
        if key == dyld:
            return dyld

        dirname, filename = os.path.split(key)
        return frameworkMap.get(filename)

    macho.rewriteLoadCommands(changefunc)
    macho.write(open(dyld, "rb+"))
Ejemplo n.º 8
0
def get_binary_architectures(filename):
    """
    Inspects the given binary and returns tuple (is_fat, archs), where is_fat is boolean indicating fat/thin binary,
    and arch is list of architectures with lipo/codesign compatible names.
    """
    executable = MachO(filename)
    return bool(executable.fat), [_get_arch_string(hdr.header) for hdr in executable.headers]
Ejemplo n.º 9
0
def print_file(fp, path):
    print(path, file=fp)
    m = MachO(path)
    for header in m.headers:
        seen = set()

        if header.MH_MAGIC == MH_MAGIC_64 or header.MH_MAGIC == MH_CIGAM_64:
            sz = "64-bit"
        else:
            sz = "32-bit"

        arch = CPU_TYPE_NAMES.get(header.header.cputype, header.header.cputype)

        subarch = get_cpu_subtype(header.header.cputype,
                                  header.header.cpusubtype)

        print(
            "    [%s endian=%r size=%r arch=%r subarch=%r]" %
            (header.__class__.__name__, header.endian, sz, arch, subarch),
            file=fp,
        )
        for _idx, _name, other in header.walkRelocatables():
            if other not in seen:
                seen.add(other)
                print("\t" + other, file=fp)
    print("", file=fp)
Ejemplo n.º 10
0
def is_macho(path):
    """Return True if the given path is a Mach-O binary."""
    try:
        MachO(path)
        return True
    except ValueError as e:
        # Grrr, why isn't macholib raising proper exceptions...
        assert str(e).startswith("Unknown Mach-O")
        return False
Ejemplo n.º 11
0
def macho_dependencies_list(target_path, header_magic=None):
    """ Generates a list of libraries the given Mach-O file depends on.

	In that list a single library is represented by its "install path": for some
	libraries it would be a full file path, and for others it would be a relative
	path (sometimes with dyld templates like @executable_path or @rpath in it).

	Note: I don't know any reason why would some architectures of a fat Mach-O depend
	on certain libraries while others don't, but *it's technically possible*.
	So that's why you may want to specify the `header_magic` value for a particular header.

	Returns an object with two properties: `weak` and `strong` that hold lists of weak
	and strong dependencies respectively.
	"""
    MachODeprendencies = namedtuple("MachODeprendecies", "weak strong")

    # Convert the magic value into macholib representation if needed
    if isinstance(header_magic, basestring):
        header_magic = _MH_MAGIC_from_string(header_magic)

    macho = MachO(target_path)
    # Obtain a list of headers for the required magic value (if any)
    suggestions = filter(
        lambda t: t.header.magic == header_magic
        or  # just add all headers if user didn't specifiy the magic
        header_magic == None,
        macho.headers)
    header = None if len(suggestions) <= 0 else suggestions[0]
    # filter() above *always* returns a list, so we have to check if it's empty
    if header is None:
        raise Exception(
            "Unable to find a header for the given MAGIC value in that Mach-O file"
        )
        return None

    def decodeLoadCommandData(data):
        # Also ignore trailing zeros
        return data[:data.find(b"\x00")].decode(sys.getfilesystemencoding())

    def strongReferencesFromHeader(h):
        # List of LC_LOAD_DYLIB commands
        list = filter(lambda (lc, cmd, data): lc.cmd == LC_LOAD_DYLIB,
                      h.commands)
        # Their contents (aka data) as a file path
        return map(lambda (lc, cmd, data): decodeLoadCommandData(data), list)

    def weakReferencesFromHeader(h):
        list = filter(lambda (lc, cmd, data): lc.cmd == LC_LOAD_WEAK_DYLIB,
                      h.commands)
        return map(lambda (lc, cmd, data): decodeLoadCommandData(data), list)

    strongRefs = strongReferencesFromHeader(header)
    weakRefs = weakReferencesFromHeader(header)

    return MachODeprendencies(weak=weakRefs, strong=strongRefs)
Ejemplo n.º 12
0
def set_macos_sdk_version(filename, major, minor, revision):
    """
    Overwrite the macOS SDK version declared in the given binary with the specified version.

    NOTE: currently, only version in the first arch slice is modified.
    """
    # Validate values
    assert 0 <= major <= 255, "Invalid major version value!"
    assert 0 <= minor <= 255, "Invalid minor version value!"
    assert 0 <= revision <= 255, "Invalid revision value!"
    # Open binary
    binary = MachO(filename)
    header = binary.headers[0]
    # Find version command using helper
    version_cmd = _find_version_cmd(header)
    # Write new SDK version number
    version_cmd[1].sdk = major << 16 | minor << 8 | revision
    # Write changes back.
    with open(binary.filename, 'rb+') as fp:
        binary.write(fp)
Ejemplo n.º 13
0
def fix_exe_for_code_signing(filename):
    """
    Fixes the Mach-O headers to make code signing possible.

    Code signing on OS X does not work out of the box with embedding
    .pkg archive into the executable.

    The fix is done this way:
    - Make the embedded .pkg archive part of the Mach-O 'String Table'.
      'String Table' is at end of the OS X exe file so just change the size
      of the table to cover the end of the file.
    - Fix the size of the __LINKEDIT segment.

    Mach-O format specification:

    http://developer.apple.com/documentation/Darwin/Reference/ManPages/man5/Mach-O.5.html
    """
    exe_data = MachO(filename)
    # Every load command is a tupple: (cmd_metadata, segment, [section1, section2])
    cmds = exe_data.headers[
        0].commands  # '0' - Exe contains only one architecture.
    file_size = exe_data.headers[0].size

    ## Make the embedded .pkg archive part of the Mach-O 'String Table'.
    # Data about 'String Table' is in LC_SYMTAB load command.
    for c in cmds:
        if c[0].get_cmd_name() == 'LC_SYMTAB':
            data = c[1]
            # Increase the size of 'String Table' to cover the embedded .pkg file.
            new_strsize = file_size - data.stroff
            data.strsize = new_strsize
    ## Fix the size of the __LINKEDIT segment.
    # __LINKEDIT segment data is the 4th item in the executable.
    linkedit = cmds[3][1]
    new_segsize = file_size - linkedit.fileoff
    linkedit.filesize = new_segsize
    linkedit.vmsize = new_segsize
    ## Write changes back.
    fp = open(exe_data.filename, 'rb+')
    exe_data.write(fp)
    fp.close()
Ejemplo n.º 14
0
    def target(self):
        filename = ""
        global debugger
        global target
        global allFuncsNames

        panel = NSOpenPanel.openPanel()
        panel.setCanCreateDirectories_(True)
        panel.setCanChooseDirectories_(True)
        panel.setCanChooseFiles_(True)
        panel.setAllowsMultipleSelection_(False)

        if panel.runModal() == NSOKButton:
            filename = panel.filename()
        try:
            if os.path.isdir(filename):
                #have to identify which file
                #or figure out how to open .app files using nsopen
                filename = glob.glob(filename + '/Contents/MacOS/*')[0]

                m = MachO(filename)
                for header in m.headers:
                    if header.MH_MAGIC == MH_MAGIC_64:
                        arch = 'systemArch64'
                    else:
                        arch = 'systemArch32'

                if platform.architecture()[0] == '64bit':
                    platArch = 'systemArch64'
                else:
                    platArch = 'systemArch32'
                target = debugger.CreateTarget(str(filename), arch, None, True,
                                               error)  #
                self.lldbout.setString_("[+]\tTarget set as: " + filename +
                                        "\n")
                # symtab=run_commands(command_interpreter,[str("image dump symtab "+filename.split('/')[-1])]).split('\n')
                # symtab=symtab[7:]

                allFuncs = returnFuntions(target, filename)
                for i in allFuncs:
                    allFuncsNames.append(i.name)

                self.ds.addObjectsFromArray_(allFuncsNames)
                self.tv.setDataSource_(self)
                self.tv.setDelegate_(self)
                self.lldbout.setString_(
                    str(self.lldbout.string()) +
                    "\n[+]\tCollecting functions\n")
                self.lldbout.setString_(
                    str(self.lldbout.string()) + "[+]\tAnalysis Done\n")

        except:
            alert('There was an error while setting target. Please try again!')
Ejemplo n.º 15
0
def get_macho_filetype(filepath):
    try:    
        macho = MachO(filepath)
    
    except (ValueError, struct.error, IOError):
        return set()

    types = set()
    for header in macho.headers:
        types.add(header.filetype)

    return types
Ejemplo n.º 16
0
 def __init__(self, name, fileobj=None):
     MachO.__init__(self, name)
     if fileobj:
         MachO.load(self, fileobj)
     else:
         with open(name, 'rb') as fp:
             MachO.load(self, fp)
Ejemplo n.º 17
0
 def __init__(self, name, fileobj=None):
     MachO.__init__(self, name)
     if fileobj:
         MachO.load(self, fileobj)
     else:
         with open(name, 'rb') as fp:
             MachO.load(self, fp)
Ejemplo n.º 18
0
def modify_object_macholib(cur_path, paths_to_paths):
    """
    This function is used when install machO buildcaches on linux by
    rewriting mach-o loader commands for dependency library paths of
    mach-o binaries and the id path for mach-o libraries.
    Rewritting of rpaths is handled by replace_prefix_bin.
    Inputs
    mach-o binary to be modified
    dictionary mapping paths in old install layout to new install layout
    """

    dll = MachO(cur_path)

    changedict = paths_to_paths

    def changefunc(path):
        npath = changedict.get(path, None)
        return npath

    dll.rewriteLoadCommands(changefunc)

    try:
        f = open(dll.filename, 'rb+')
        for header in dll.headers:
            f.seek(0)
            dll.write(f)
        f.seek(0, 2)
        f.flush()
        f.close()
    except Exception:
        pass

    return
Ejemplo n.º 19
0
def fix_exe_for_code_signing(filename):
    """
    Fixes the Mach-O headers to make code signing possible.

    Code signing on OS X does not work out of the box with embedding
    .pkg archive into the executable.

    The fix is done this way:
    - Make the embedded .pkg archive part of the Mach-O 'String Table'.
      'String Table' is at end of the OS X exe file so just change the size
      of the table to cover the end of the file.
    - Fix the size of the __LINKEDIT segment.

    Mach-O format specification:

    http://developer.apple.com/documentation/Darwin/Reference/ManPages/man5/Mach-O.5.html
    """
    exe_data = MachO(filename)
    # Every load command is a tupple: (cmd_metadata, segment, [section1, section2])
    cmds = exe_data.headers[0].commands  # '0' - Exe contains only one architecture.
    file_size = exe_data.headers[0].size

    ## Make the embedded .pkg archive part of the Mach-O 'String Table'.
    # Data about 'String Table' is in LC_SYMTAB load command.
    for c in cmds:
        if c[0].get_cmd_name() == 'LC_SYMTAB':
            data = c[1]
            # Increase the size of 'String Table' to cover the embedded .pkg file.
            new_strsize = file_size - data.stroff
            data.strsize = new_strsize
    ## Fix the size of the __LINKEDIT segment.
    # __LINKEDIT segment data is the 4th item in the executable.
    linkedit = cmds[3][1]
    new_segsize = file_size - linkedit.fileoff
    linkedit.filesize = new_segsize
    linkedit.vmsize = new_segsize
    ## Write changes back.
    fp = open(exe_data.filename, 'rb+')
    exe_data.write(fp)
    fp.close()
Ejemplo n.º 20
0
def get_macos_sdk_version(filename):
    """
    Obtain the version of macOS SDK against which the given binary was built.

    NOTE: currently, version is retrieved only from the first arch slice in the binary.

    :return: (major, minor, revision) tuple
    """
    binary = MachO(filename)
    header = binary.headers[0]
    # Find version command using helper
    version_cmd = _find_version_cmd(header)
    return _hex_triplet(version_cmd[1].sdk)
Ejemplo n.º 21
0
def processFile(filename):
    res = defaultdict()

    try:
        machoEntropyComputer = MachoEntropyComputer(filename, MachO(filename))
    except:
        print "ERROR while parsing", filename
        return None
    for s in machoEntropyComputer.compute():
        print "%s has %i bytes with entropy of %f" % (
            s.sectionName, s.sectionSize, s.sectionEntropy)
        res[s.sectionName] = s.sectionEntropy
    return res
Ejemplo n.º 22
0
def get_macho_load_commands(filepath):
    commands = set()

    try:    
        macho = MachO(filepath)
    
    except (ValueError, struct.error):
        return set()

    for header in macho.headers:
        for command in header.commands:
            commands.add(command[0].get_cmd_name())

    return commands
Ejemplo n.º 23
0
def splitFatBinaryFile(path, outputPath):
    logger = utils.setLogger()
    if not utils.isFatBinary(path):
        logger.info("this file is not fat binary file,can not be splited")
        return

    m = MachO(path)
    for header in m.headers:
        utils.getPartOfFile(path, header.offset, header.size)
        fin = open("temp", "rb")
        s1 = hashlib.sha1(fin.read()).hexdigest()
        fin.close()
        os.rename("temp", outputPath + "/" + s1)
    logger.info("this fat binary file has been splited sucessfully")
Ejemplo n.º 24
0
def macho_archs(
        filename: typing.Union[os.PathLike[str], str]) -> typing.Set[str]:
    result = set()

    m = MachO(os.fspath(filename))
    for hdr in m.headers:
        arch = CPU_TYPE_NAMES[hdr.header.cputype]
        if arch == "PowerPC":
            arch = "ppc"
        elif arch == "PowerPC64":
            arch = "ppc64"
        result.add(arch)

    return result
Ejemplo n.º 25
0
def detect_macho_type(path):
    """
    Returns None if not a mach-o.
    """
    try:
        p = MachO(path)
    except ValueError as e:
        # Grrr, why isn't macholib raising proper exceptions...
        assert str(e).startswith("Unknown Mach-O")
        return None
    else:
        if len(p.headers) < 1:
            raise ValueError("No headers in the mach-o file ?")
        else:
            return p.headers[0].filetype
Ejemplo n.º 26
0
def macosx_version_min(filename: str) -> tuple:
    """
    Get the -macosx-version-min used to compile a macOS binary.

    For fat binaries, the minimum version is selected.
    """
    versions = []
    for header in MachO(filename).headers:
        cmd = _find_version_cmd(header)
        if cmd[0].cmd == LC_VERSION_MIN_MACOSX:
            versions.append(cmd[1].version)
        else:
            # macOS >= 10.14 uses LC_BUILD_VERSION instead.
            versions.append(cmd[1].minos)

    return min(map(_hex_triplet, versions))
Ejemplo n.º 27
0
def print_file(fp, path):
    print >>fp, path
    m = MachO(path)
    for header in m.headers:
        seen = set()
        if header.MH_MAGIC == MH_MAGIC_64:
            sz = '64-bit'
        else:
            sz = '32-bit'

        print >>fp, '    [%s endian=%r size=%r arch=%r]' % (header.__class__.__name__, 
                header.endian, sz, ARCH_MAP[(header.endian, sz)])
        for idx, name, other in header.walkRelocatables():
            if other not in seen:
                seen.add(other)
                print >>fp, '\t' + other
def get_bin_info(bin_file):
    """Get Binary Information."""
    logger.info('Getting Binary Information')
    m = MachO(bin_file)
    for header in m.headers:
        if header.MH_MAGIC == MH_MAGIC_64 or header.MH_MAGIC == MH_CIGAM_64:
            sz = '64-bit'
        else:
            sz = '32-bit'
        arch = CPU_TYPE_NAMES.get(
            header.header.cputype, header.header.cputype)
        subarch = get_cpu_subtype(
            header.header.cputype, header.header.cpusubtype)
        return {'endian': header.endian,
                'bit': sz,
                'arch': arch,
                'subarch': subarch}
Ejemplo n.º 29
0
def init_macho_info(macho_file):
    """

    :param macho_file:
    :return:
    """
    macho_obj = MachO(macho_file)
    for (_load_cmd, cmd, data) in macho_obj.headers[0].commands:
        try:
            segname = getattr(cmd, 'segname')
        except AttributeError:
            continue
        if segname.startswith(b'__TEXT'):
            params = dict()
            for _index, section in enumerate(data):
                sect_name = getattr(section, 'sectname')
                if sect_name.startswith(b'__text'):
                    text_offset = getattr(section, 'offset')
                    text_size = getattr(section, 'size')
                    params['text_offset'] = text_offset
                    params['text_size'] = text_size
                if sect_name.startswith(b'__objc_classname'):
                    class_offset = getattr(section, 'offset')
                    class_size = getattr(section, 'size')
                    params['class_offset'] = class_offset
                    params['class_size'] = class_size
                if sect_name.startswith(b'__objc_methname'):
                    methname_offset = getattr(section, 'offset')
                    methname_size = getattr(section, 'size')
                    params['methname_offset'] = methname_offset
                    params['methname_size'] = methname_size
                if sect_name.startswith(b'__cstring'):
                    cstring_offset = getattr(section, 'offset')
                    cstring_size = getattr(section, 'size')
                    params['cstring_offset'] = cstring_offset
                    params['cstring_size'] = cstring_size
                if sect_name.startswith(b'__objc_methtype'):
                    methtype_offset = getattr(section, 'offset')
                    methtype_size = getattr(section, 'size')
                    params['methtype_offset'] = methtype_offset
                    params['methtype_size'] = methtype_size
            return params
    return None
Ejemplo n.º 30
0
def getData(pathToSamples, c):
    df = pd.DataFrame(columns=('f_size', 'f_sections', 'f_flags', 'c'))
    currentRow = 0

    for f in os.listdir(pathToSamples):
        if not ".json" in f:
            fullpath = os.path.join(pathToSamples, f)
            f_size = getSizeInKiloByte(fullpath)

            try:
                macho = MachO(fullpath)
                f_sections = getSectionCount(macho)
                f_flags = macho.headers[0].header.flags
            except:
                continue

            df.loc[currentRow] = [f_size, f_sections, f_flags, c]
            currentRow += 1

    return df
Ejemplo n.º 31
0
def mac_is_binary_signed(filename):
    """
    Check if the given macOS binary file is signed.
    """
    from macholib.MachO import MachO
    from macholib import mach_o  # constants

    # Open the file
    try:
        m = MachO(filename)
    except Exception:
        return False

    # Walk over all headers and check if any contains LC_CODE_SIGNATURE
    # load command
    for header in m.headers:
        for cmd in header.commands:
            if cmd[0].cmd == mach_o.LC_CODE_SIGNATURE:
                return True
    return False
Ejemplo n.º 32
0
def print_file(fp, path):
    print(path, file=fp)
    m = MachO(path)
    for header in m.headers:
        seen = set()
        if header.MH_MAGIC == MH_MAGIC_64:
            sz = '64-bit'
        else:
            sz = '32-bit'

        arch = CPU_TYPE_NAMES.get(header.header.cputype, header.header.cputype)

        print('    [%s endian=%r size=%r arch=%r]' %
              (header.__class__.__name__, header.endian, sz, arch),
              file=fp)
        for idx, name, other in header.walkRelocatables():
            if other not in seen:
                seen.add(other)
                print('\t' + other, file=fp)
    print('', file=fp)
Ejemplo n.º 33
0
def get_macho_section_name(filepath):
    section_types = set()

    try:    
        macho = MachO(filepath)
    
    except (ValueError, struct.error, IOError):
        return set()

    for header in macho.headers:
        for command in header.commands:
            try:
                if command[1].nsects:
                    for sect in command[2]:
                        if sect.size:
                            segname = sect.segname.replace("\x00", "")
                            sectname = sect.sectname.replace("\x00", "")
                            section_types.add("%s.%s" % (segname, sectname))

            except AttributeError, e:
                pass
Ejemplo n.º 34
0
def rewriteFramework(framework, frameworkMap):

    basename = os.path.splitext(os.path.basename(framework))[0]
    dyld = os.path.abspath(os.path.join(framework, basename))

    macho = MachO(dyld)

    def changefunc(key):
        if key == dyld:
            return dyld

        dirname, filename = os.path.split(key)
        return frameworkMap.get(filename)

    macho.rewriteLoadCommands(changefunc)
    macho.write(open(dyld, "rb+"))
Ejemplo n.º 35
0
class CMachoFuzzer:
  def __init__(self):
    self.macho = None
    self.fuzz_properties = ["headers"]
    self.fuzz_sub_properties = {"headers":["header", "commands", "headers"]}
    
    self.changes = []
    self.change_list = []

  def do_fuzz_headers(self):
    # Select a random header
    header = random.choice(self.macho.headers)
    idx = self.macho.headers.index(header)
    self.changes.append(["Header %d" % idx])
    prop = random.choice(self.fuzz_sub_properties["headers"])

    if prop == "header":
      fields = random.choice(header.header._fields_)
      field = fields[0]
      
      change_name = "header %d field %s" % (idx, field)
      if change_name in self.change_list or field in BANNED_FIELDS:
        #print "Ignoring already applied change %s" % change_name
        del self.changes[len(self.changes)-1]
        return

      self.changes[len(self.changes)-1].append("Field %s" % field)
      l = "header.header.%s = %d" % (field, get_random_value(fields[1]))
      exec(l)
      
      self.change_list.append(change_name)
    elif prop == "commands":
      cmd = random.choice(header.commands)
      idx = header.commands.index(cmd)
      self.changes[len(self.changes)-1].append("Command %d" % idx)
      
      subidx = random.randint(0, len(cmd)-1)
      subcmd = cmd[subidx]

      if '_fields_' in dir(subcmd):
        if len(subcmd._fields_) > 0:
          fields = random.choice(subcmd._fields_)
          field = fields[0]
          self.changes[len(self.changes)-1].append("Field %s" % field)
          str_type = str(type(eval("subcmd.%s" % field)))
          if str_type in SUPPORTED_FIELD_TYPES:
            l = "subcmd.%s = " % field
            l += str(get_random_value(fields[1]))
            exec(l)
          else:
            #print "Ignoring unsupported field type", str_type, field
            del self.changes[len(self.changes)-1]
        else:
          print "Ignoring empty subcmd", subcmd
          del self.changes[len(self.changes)-1]
      elif type(subcmd) is str:
        #print "Ignoring unsupported (by macholib) string sub-command"
        del self.changes[len(self.changes)-1]
      else:
        print type(subcmd), subcmd
        if type(subcmd) is list and len(subcmd) > 0:
          field = random.choice(subcmd)
          subidx = subcmd.index(field)
          self.changes[len(self.changes)-1].append("List element %d" % subcmd.index(field))

          fields = random.choice(field._fields_)
          field_name = fields[0]
          self.changes[len(self.changes)-1].append("Field %s" % field_name)

          l = "field.%s = " % field_name
          l += str(get_random_value(fields[1]))
          exec(l)
        else:
          del self.changes[len(self.changes)-1]
      #self.changes[len(self.changes)-1].append("Sub-command %d" % sub_idx)
    elif prop == "headers":
      del self.changes[len(self.changes)-1]
      #print "Not yet supported headers"
      #raise Exception("Implement headers")
    else:
      del self.changes[len(self.changes)-1]

  def do_fuzz_internal(self):
    assert(self.macho is not None)
    
    element = random.choice(self.fuzz_properties)
    if element == "headers":
      self.do_fuzz_headers()
    else:
      raise Exception("Unknown element to fuzz %s" % repr(element))

  def fuzz(self, filename, output_filename):
    self.macho = MachO(filename)

    changes = random.randint(1, 25)
    for i in range(changes*5):
      self.do_fuzz_internal()
      if len(self.change_list) == changes:
        break

    # Copy the contents of the original file to the output file
    f = open(output_filename, "wb+")
    f.write(open(filename, "rb").read())
    f.close()

    # Update it's contents
    f = open(output_filename, "rb+")
    self.macho.write(f)
    f.close()

    # And write the .diff file
    f = open(output_filename + ".diff", "wb")
    f.write("# Original file created by 'MachO Mutator' was %s\n" % filename)
    for change in self.changes:
      print "# CHANGE: %s" % ", ".join(change)
      f.write("# CHANGE: %s\n" % ", ".join(change))
    f.close()
    
    os.system("radiff2 %s %s" % (filename, output_filename))
Ejemplo n.º 36
0
def mac_set_relative_dylib_deps(libname, distname):
    """
    On Mac OS X set relative paths to dynamic library dependencies
    of `libname`.

    Relative paths allow to avoid using environment variable DYLD_LIBRARY_PATH.
    There are known some issues with DYLD_LIBRARY_PATH. Relative paths is
    more flexible mechanism.

    Current location of dependend libraries is derived from the location
    of the library path (paths start with '@loader_path').

    'distname'  path of the library relative to dist directory of frozen
                executable. We need this to determine the level of directory
                level for @loader_path of binaries not found in dist directory.

                E.g. qt4 plugins are not in the same directory as Qt*.dylib
                files. Without using '@loader_path/../..' for qt plugins
                Mac OS X would not be able to resolve shared library
                dependencies and qt plugins will not be loaded.
    """

    from macholib import util
    from macholib.MachO import MachO

    # Ignore bootloader otherwise PyInstaller fails with exception like
    # 'ValueError: total_size > low_offset (288 > 0)'
    if os.path.basename(libname) in _BOOTLOADER_FNAMES:
        return

    # Determine how many directories up is the directory with shared
    # dynamic libraries. '../'
    # E.g.  ./qt4_plugins/images/ -> ./../../
    parent_dir = ''
    # Check if distname is not only base filename.
    if os.path.dirname(distname):
        parent_level = len(os.path.dirname(distname).split(os.sep))
        parent_dir = parent_level * (os.pardir + os.sep)

    def match_func(pth):
        """
        For system libraries is still used absolute path. It is unchanged.
        """
        # Match non system dynamic libraries.
        if not util.in_system_path(pth):
            # Use relative path to dependend dynamic libraries bases on
            # location of the executable.
            return os.path.join('@loader_path', parent_dir,
                os.path.basename(pth))

    # Rewrite mach headers with @loader_path.
    dll = MachO(libname)
    dll.rewriteLoadCommands(match_func)

    # Write changes into file.
    # Write code is based on macholib example.
    try:
        with open(dll.filename, 'rb+') as f:
            for header in dll.headers:
                f.seek(0)
                dll.write(f)
            f.seek(0, 2)
            f.flush()
    except Exception:
        pass