示例#1
0
        /// <summary>
        /// This Method is a utility to remove the code-signature (if any)
        /// from a MachO AppHost binary.
        ///
        /// The tool assumes the following layout of the executable:
        ///
        /// * MachoHeader (64-bit, executable, not swapped integers)
        /// * LoadCommands
        ///     LC_SEGMENT_64 (__PAGEZERO)
        ///     LC_SEGMENT_64 (__TEXT)
        ///     LC_SEGMENT_64 (__DATA)
        ///     LC_SEGMENT_64 (__LINKEDIT)
        ///     ...
        ///     LC_SYMTAB
        ///     ...
        ///     LC_CODE_SIGNATURE (last)
        ///
        ///  * ... Different Segments ...
        ///
        ///  * The __LINKEDIT Segment (last)
        ///      * ... Different sections ...
        ///      * SYMTAB
        ///      * (Some alignment bytes)
        ///      * The Code-signature
        ///
        /// In order to remove the signature, the method:
        /// - Removes (zeros out) the LC_CODE_SIGNATURE command
        /// - Adjusts the size and count of the load commands in the header
        /// - Truncates the size of the __LINKEDIT segment to the end of SYMTAB
        /// - Truncates the apphost file to the end of the __LINKEDIT segment
        ///
        /// </summary>
        /// <param name="filePath">Path to the AppHost</param>
        /// <returns>
        ///  True if
        ///    - The input is a MachO binary, and
        ///    - It is a signed binary, and
        ///    - The signature was successfully removed
        ///   False otherwise
        /// </returns>
        /// <exception cref="AppHostMachOFormatException">
        /// The input is a MachO file, but doesn't match the expect format of the AppHost.
        /// </exception>
        unsafe public static bool RemoveSignature(string filePath)
        {
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite))
            {
                uint signatureSize = 0;
                using (var mappedFile = MemoryMappedFile.CreateFromFile(stream,
                                                                        mapName: null,
                                                                        capacity: 0,
                                                                        MemoryMappedFileAccess.ReadWrite,
                                                                        HandleInheritability.None,
                                                                        leaveOpen: true))
                {
                    using (var accessor = mappedFile.CreateViewAccessor())
                    {
                        byte *file = null;
                        RuntimeHelpers.PrepareConstrainedRegions();
                        try
                        {
                            accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref file);
                            Verify(file != null, MachOFormatError.MemoryMapAccessFault);

                            MachHeader *header = (MachHeader *)file;

                            if (!header->IsValid())
                            {
                                // Not a MachO file.
                                return(false);
                            }

                            Verify(header->Is64BitExecutable(), MachOFormatError.Not64BitExe);

                            file += sizeof(MachHeader);
                            SegmentCommand64 *   linkEdit  = null;
                            SymtabCommand *      symtab    = null;
                            LinkEditDataCommand *signature = null;

                            for (uint i = 0; i < header->ncmds; i++)
                            {
                                LoadCommand *command = (LoadCommand *)file;
                                if (command->cmd == Command.LC_SEGMENT_64)
                                {
                                    SegmentCommand64 *segment = (SegmentCommand64 *)file;
                                    if (segment->SegName.Equals("__LINKEDIT"))
                                    {
                                        Verify(linkEdit == null, MachOFormatError.DuplicateLinkEdit);
                                        linkEdit = segment;
                                    }
                                }
                                else if (command->cmd == Command.LC_SYMTAB)
                                {
                                    Verify(symtab == null, MachOFormatError.DuplicateSymtab);
                                    symtab = (SymtabCommand *)command;
                                }
                                else if (command->cmd == Command.LC_CODE_SIGNATURE)
                                {
                                    Verify(i == header->ncmds - 1, MachOFormatError.SignCommandNotLast);
                                    signature = (LinkEditDataCommand *)command;
                                    break;
                                }

                                file += command->cmdsize;
                            }

                            if (signature != null)
                            {
                                Verify(linkEdit != null, MachOFormatError.SignNeedsLinkEdit);
                                Verify(symtab != null, MachOFormatError.SignNeedsSymtab);

                                var symtabEnd    = symtab->stroff + symtab->strsize;
                                var linkEditEnd  = linkEdit->fileoff + linkEdit->filesize;
                                var signatureEnd = signature->dataoff + signature->datasize;
                                var fileEnd      = (ulong)stream.Length;

                                Verify(linkEditEnd == fileEnd, MachOFormatError.LinkEditNotLast);
                                Verify(signatureEnd == fileEnd, MachOFormatError.SignBlobNotLast);

                                Verify(symtab->symoff > linkEdit->fileoff, MachOFormatError.SymtabNotInLinkEdit);
                                Verify(signature->dataoff > linkEdit->fileoff, MachOFormatError.SignNotInLinkEdit);

                                // The signature blob immediately follows the symtab blob,
                                // except for a few bytes of padding.
                                Verify(signature->dataoff >= symtabEnd && signature->dataoff - symtabEnd < 32, MachOFormatError.SignBlobNotLast);

                                // Remove the signature command
                                header->ncmds--;
                                header->sizeofcmds -= signature->cmdsize;
                                Unsafe.InitBlock(signature, 0, signature->cmdsize);

                                // Remove the signature blob (note for truncation)
                                signatureSize = (uint)(fileEnd - symtabEnd);

                                // Adjust the __LINKEDIT segment load command
                                linkEdit->filesize -= signatureSize;

                                // codesign --remove-signature doesn't reset the vmsize.
                                // Setting the vmsize here makes the output bin-equal with the original
                                // unsigned apphost (and not bin-equal with a signed-unsigned-apphost).
                                linkEdit->vmsize = linkEdit->filesize;
                            }
                        }
                        finally
                        {
                            if (file != null)
                            {
                                accessor.SafeMemoryMappedViewHandle.ReleasePointer();
                            }
                        }
                    }
                }

                if (signatureSize != 0)
                {
                    // The signature was removed, update the file length
                    stream.SetLength(stream.Length - signatureSize);
                    return(true);
                }

                return(false);
            }
        }
示例#2
0
        /// <summary>
        /// This Method is a utility to adjust the apphost MachO-header
        /// to include the bytes added by the single-file bundler at the end of the file.
        ///
        /// The tool assumes the following layout of the executable
        ///
        /// * MachoHeader (64-bit, executable, not swapped integers)
        /// * LoadCommands
        ///     LC_SEGMENT_64 (__PAGEZERO)
        ///     LC_SEGMENT_64 (__TEXT)
        ///     LC_SEGMENT_64 (__DATA)
        ///     LC_SEGMENT_64 (__LINKEDIT)
        ///     ...
        ///     LC_SYMTAB
        ///
        ///  * ... Different Segments
        ///
        ///  * The __LINKEDIT Segment (last)
        ///      * ... Different sections ...
        ///      * SYMTAB (last)
        ///
        /// The MAC codesign tool places several restrictions on the layout
        ///   * The __LINKEDIT segment must be the last one
        ///   * The __LINKEDIT segment must cover the end of the file
        ///   * All bytes in the __LINKEDIT segment are used by other linkage commands
        ///     (ex: symbol/string table, dynamic load information etc)
        ///
        /// In order to circumvent these restrictions, we:
        ///    * Extend the __LINKEDIT segment to include the bundle-data
        ///    * Extend the string table to include all the bundle-data
        ///      (that is, the bundle-data appear as strings to the loader/codesign tool).
        ///
        ///  This method has certain limitations:
        ///    * The bytes for the bundler may be unnecessarily loaded at startup
        ///    * Tools that process the string table may be confused (?)
        ///    * The string table size is limited to 4GB. Bundles larger than that size
        ///      cannot be accomodated by this utility.
        ///
        /// </summary>
        /// <param name="filePath">Path to the AppHost</param>
        /// <returns>
        ///  True if
        ///    - The input is a MachO binary, and
        ///    - The additional bytes were successfully accomodated within the MachO segments.
        ///   False otherwise
        /// </returns>
        /// <exception cref="AppHostMachOFormatException">
        /// The input is a MachO file, but doesn't match the expect format of the AppHost.
        /// </exception>
        public static unsafe bool AdjustHeadersForBundle(string filePath)
        {
            ulong fileLength = (ulong)new FileInfo(filePath).Length;

            using (var mappedFile = MemoryMappedFile.CreateFromFile(filePath))
            {
                using (var accessor = mappedFile.CreateViewAccessor())
                {
                    byte *file = null;
                    try
                    {
                        accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref file);
                        Verify(file != null, MachOFormatError.MemoryMapAccessFault);

                        MachHeader *header = (MachHeader *)file;

                        if (!header->IsValid())
                        {
                            // Not a MachO file.
                            return(false);
                        }

                        Verify(header->Is64BitExecutable(), MachOFormatError.Not64BitExe);

                        file += sizeof(MachHeader);
                        SegmentCommand64 *   linkEdit  = null;
                        SymtabCommand *      symtab    = null;
                        LinkEditDataCommand *signature = null;

                        for (uint i = 0; i < header->ncmds; i++)
                        {
                            LoadCommand *command = (LoadCommand *)file;
                            if (command->cmd == Command.LC_SEGMENT_64)
                            {
                                SegmentCommand64 *segment = (SegmentCommand64 *)file;
                                if (segment->SegName.Equals("__LINKEDIT"))
                                {
                                    Verify(linkEdit == null, MachOFormatError.DuplicateLinkEdit);
                                    linkEdit = segment;
                                }
                            }
                            else if (command->cmd == Command.LC_SYMTAB)
                            {
                                Verify(symtab == null, MachOFormatError.DuplicateSymtab);
                                symtab = (SymtabCommand *)command;
                            }

                            file += command->cmdsize;
                        }

                        Verify(linkEdit != null, MachOFormatError.MissingLinkEdit);
                        Verify(symtab != null, MachOFormatError.MissingSymtab);

                        // Update the string table to include bundle-data
                        ulong newStringTableSize = fileLength - symtab->stroff;
                        if (newStringTableSize > uint.MaxValue)
                        {
                            // Too big, too bad;
                            return(false);
                        }
                        symtab->strsize = (uint)newStringTableSize;

                        // Update the __LINKEDIT segment to include bundle-data
                        linkEdit->filesize = fileLength - linkEdit->fileoff;
                        linkEdit->vmsize   = linkEdit->filesize;
                    }
                    finally
                    {
                        if (file != null)
                        {
                            accessor.SafeMemoryMappedViewHandle.ReleasePointer();
                        }
                    }
                }
            }

            return(true);
        }