public override void WriteData(WritingContext SW)
			{
				base.WriteData(SW);
				SW.Write(CertificateIndex);													// index in the mobile provision certificate list (always 0 for now)
				SW.Write(FieldName.Length);													// field name length
				SW.WriteFixedASCII(FieldName, FieldName.Length);							// field name to match
				int Count = 4 - FieldName.Length % 4;										// may need to pad to alignment of 4 bytes
				if (Count > 0 && Count < 4)
					SW.WriteZeros(Count);
				SW.Write(MatchOp.MatchOp);													// must equal
				SW.Write(MatchOp.CertificateName.Length);									// length of certficate name
				SW.WriteFixedASCII(MatchOp.CertificateName, MatchOp.CertificateName.Length);// certificate name to match
				Count = 4 - MatchOp.CertificateName.Length % 4;								// may need to pad to alignment of 4 bytes
				if (Count > 0 && Count < 4)
					SW.WriteZeros(Count);
			}
Exemple #2
0
		/// <summary>
		/// Patches just the file length of this segment in an existing file
		/// </summary>
		public void PatchFileLength(WritingContext SW, UInt32 NewLength)
		{
			Debug.Assert(StartingLoadOffset >= 0);

			if (Config.bCodeSignVerbose)
			{
				Console.WriteLine("ZZZZZZZZZZZZZZZZ");
				Console.WriteLine(" Segment Length from 0x{0:X} to 0x{1:X} (delta {2})", FileSize, NewLength, (long)NewLength - (long)FileSize);
				Console.WriteLine("ZZZZZZZZZZZZZZZZ");
			}

			FileSize = NewLength;

			long PatchOffset = StartingLoadOffset + (2 * sizeof(UInt32)); // Command, CommandLength
			PatchOffset += 16; // SegmentName
			PatchOffset += 3 * Bits.Bytes(AddressSize); // VirtualAddress, VirtualSize, FileOffset

			SW.PushPositionAndJump(PatchOffset);
			SW.WriteUInt(FileSize, AddressSize);
			SW.PopPosition();
		}
Exemple #3
0
		protected override void PackageData(WritingContext SW)
		{
			SW.Write(SymbolTableOffset);
			SW.Write(SymbolCount);
			SW.Write(StringTableOffset);
			SW.Write(StringTableSize);
		}
Exemple #4
0
		protected abstract void PackageData(WritingContext SW);
Exemple #5
0
		protected override void PackageData(WritingContext SW)
		{
			SW.Write(MyCommandData, 0, MyCommandData.Length);
		}
Exemple #6
0
		public DeferredFieldU32or64(WritingContext Context, long Base, Bits.Num Address)
		{
			AnchorPoint = Base;
			WritePoint = Context.Position;

			AddressSize = Address;
			Context.Position += Bits.Bytes(AddressSize);
		}
Exemple #7
0
		public OffsetFieldU32or64(WritingContext Context, long BaseOffset, Bits.Num Address)
			: base(Context, BaseOffset, Address)
		{
		}
Exemple #8
0
		public void Write(WritingContext Context)
		{
			// Write the header
			Context.Write(Magic);
			Context.Write(CpuType);
			Context.Write(CpuSubType);
			Context.Write(FileType);

			Context.Write((UInt32)Commands.Count);
			LengthFieldU32or64 SizeOfCommands = Context.WriteDeferredLength(-2 * sizeof(UInt32), Bits.Num._32); // Size of commands, after this field and the flags field
			
			Context.Write(Flags);
			if (Magic == MH_MAGIC_64)
			{
				Context.Write(Reserved64);
			}

			// Write each command (which may enqueue deferred work)
			foreach (MachLoadCommand Command in Commands)
			{
				Command.Write(Context);
			}
			Context.CommitDeferredField(SizeOfCommands);

			//@TODO: Figure out where this offsetting comes from
			long MainStartPosition = MachHeaderPad;
			Context.WriteZeros(MainStartPosition - Context.Position);

			// Drain deferred work until the file is completely done
			Context.CompleteWritingAndClose();
		}
Exemple #9
0
		public void Write(WritingContext SW)
		{
			SW.Write(MyMagic);
			
			LengthFieldU32or64 Length = SW.WriteDeferredLength(4, Bits.Num._32);
			PackageData(SW);
			SW.CommitDeferredField(Length);
		}
Exemple #10
0
		protected override void PackageData(WritingContext SW)
		{
			SW.Write(EncryptedFileOffset);
			SW.Write(EncryptedFileSize);
			SW.Write(EncryptionMode);
		}
Exemple #11
0
		// 0,2,4,6,8 are offsets :(

		protected override void PackageData(WritingContext SW)
		{
			for (int i = 0; i < Payload.Length; ++i)
			{
				SW.Write(Payload[i]);
			}
		}
		protected override void PackageData(WritingContext SW)
		{
			// update all of the read expressions with the certificate name and bundle identifier
			Expression.UpdateCertificateAndBundle(CertificateName, BundleIdentifier);
			SW.Write(kReqExpression);
			Expression.WriteData(SW);
		}
			public override void WriteData(WritingContext SW)
			{
				base.WriteData(SW);
				SW.Write(CertificateIndex);											// index of the OID value (always 1)
				SW.Write(Hash.Length);										// length of OID
				SW.Write(Hash);						// OID to match
				int Count = 4 -  Hash.Length % 4;										// may need to pad to alignment of 4 bytes
				if (Count > 0 && Count < 4)
					SW.WriteZeros(Count);
			}
			public override void WriteData(WritingContext SW)
			{
				base.WriteData(SW);
				SW.Write(OIDIndex);											// index of the OID value (always 1)
				SW.Write(OID.Length);										// length of OID
				SW.Write(OID);						// OID to match
				int Count = 4 - OID.Length % 4;										// may need to pad to alignment of 4 bytes
				if (Count > 0 && Count < 4)
					SW.WriteZeros(Count);

				// may need to pad to alignment of 4 bytes
				SW.Write(MatchOp.MatchOp);										// OID must exist
			}
Exemple #15
0
		public void Drain(WritingContext Context)
		{
			if (Config.bCodeSignVerbose)
			{
				Console.WriteLine("Draining phase with {0} writes", PendingWrites.Count);
			}

			while (PendingWrites.Count > 0)
			{
				if (Config.bCodeSignVerbose)
				{
					Console.WriteLine("  One delayed write at position = 0x{0:X}", Context.Position);
				}

				DelayedWriteCallback Job = PendingWrites.Dequeue();
				Job.Invoke(Context);
			}

			if (Config.bCodeSignVerbose)
			{
				Console.WriteLine("Finished draining phase (curpos = 0x{0:X})", Context.Position);
			}
		}
Exemple #16
0
		/// <summary>
		/// Always uses big endian!!!
		/// Converts a blob back into an array of bytes (does not work if there are any file-absolute offsets, as it serializes starting at 0 of a fake stream)
		/// </summary>
		public byte[] GetBlobBytes()
		{
			MemoryStream MemoryBuffer = new MemoryStream();
			BinaryWriter Writer = new BinaryWriter(MemoryBuffer);
			WritingContext SW = new WritingContext(Writer);
			SW.bStreamLittleEndian = false;

			Write(SW);
			SW.CompleteWritingAndClose();

			return MemoryBuffer.ToArray();
		}
Exemple #17
0
		public void WriteToFile(string Filename)
		{
			BinaryWriter SW = new BinaryWriter(File.OpenWrite(Filename));
			WritingContext Context = new WritingContext(SW);
			Write(Context);
			SW.Close();
		}
Exemple #18
0
		protected override void PackageData(WritingContext SW)
		{
			SW.Write(MyData);
		}
Exemple #19
0
		public void Commit(WritingContext Context)
		{
			long CurrentPos = Context.Position;
			long Length = CurrentPos - AnchorPoint;

			if (AddressSize == Bits.Num._32)
			{
				Debug.Assert((Length >= UInt32.MinValue) && (Length <= UInt32.MaxValue));
			}

			Context.PushPositionAndJump(WritePoint);
			Context.WriteUInt((UInt64)Length, AddressSize);
			Context.PopPosition();
		}
Exemple #20
0
		protected override void PackageData(WritingContext SW)
		{
			Table.Write(SW);
		}
Exemple #21
0
		public void Write(WritingContext SW)
		{
			SW.WriteFixedASCII(SectionName, 16);
			SW.WriteFixedASCII(SegmentName, 16);
			SW.WriteUInt(Addr, AddressSize);
			SW.WriteUInt(Size, AddressSize);
			SW.WriteAbsoluteOffsetAndDelayedData(SectionData, 1 << (byte)LogAlignment, AddressSize);
			SW.Write(LogAlignment);
			SW.Write(RelocationOffset);
			SW.Write(NumRelocations);
			SW.Write((UInt32)Flags);
			SW.Write(Reserved1);
			SW.Write(Reserved2);

			if (AddressSize == Bits.Num._64)
				SW.Write(Reserved3);
		}
Exemple #22
0
		public void Write(WritingContext SW)
		{
			// Magic (written in the outer)
			// Length (written in the outer)
			// SlotCount
			// Slot SlotTable[SlotCount]
			//   Each slot is Key, Offset
			// <Slot-referenced data>

			//WritingContext.OffsetFieldU32or64[] Offsets = new WritingContext.OffsetFieldU32or64[Slots.Count];
			long StartingPosition = SW.Position - (2 * sizeof(UInt32));

			// Start a phase for writing out the superblob data
			SW.CreateNewPhase();

			// Write the slot table, queuing up the individual slot writes
			SW.Write((UInt32)Slots.Count);

			foreach (KeyValuePair<UInt32, AbstractBlob> Slot in Slots)
			{
				SW.Write(Slot.Key);

				OffsetFieldU32or64 Offset = SW.WriteDeferredOffsetFrom(StartingPosition, Bits.Num._32);
				KeyValuePair<UInt32, AbstractBlob> LocalSlot = Slot;

				SW.CurrentPhase.PendingWrites.Enqueue(delegate(WritingContext Context)
					{
						if (Config.bCodeSignVerbose)
						{
							Console.WriteLine("Writing a slot.  Offset={0}, SlotData={1}", Offset.WritePoint, LocalSlot.ToString());
						}
						SW.CommitDeferredField(Offset);
						LocalSlot.Value.Write(Context);
					});
			}

			// Force evaluation of the slots
			SW.ProcessEntirePhase();
		}
Exemple #23
0
		public void Write(WritingContext SW)
		{
			long StartingPosition = SW.Position;

			// Write the command and length
			SW.Write(Command);
			LengthFieldU32or64 Length = SW.WriteDeferredLength(4, Bits.Num._32);

			// Write the data
			PackageData(SW);

			// Write any pad bytes and commit the length
			SW.WriteZeros((SW.Position - StartingPosition) % 4);
			SW.CommitDeferredField(Length);
		}
Exemple #24
0
		protected override void PackageData(WritingContext SW)
		{
			long StartPos = SW.Position - (2 * sizeof(UInt32));

			SW.Write(Version);
			SW.Write(Flags);

			// The hash offset is weird, it points to the first code page hash, not the start of the hashes array...
			OffsetFieldU32or64 HashOffset = SW.WriteDeferredOffsetFrom(StartPos - (BytesPerHash * SpecialSlotCount), Bits.Num._32);
			OffsetFieldU32or64 IdentifierStringOffset = SW.WriteDeferredOffsetFrom(StartPos, Bits.Num._32);
			
			SW.Write(SpecialSlotCount);
			SW.Write(CodeSlotCount);
			SW.Write(MainImageSignatureLimit);
			
			SW.Write(BytesPerHash);
			SW.Write(HashType);
			SW.Write(Spare1);
			SW.Write(LogPageSize);
			
			SW.Write(Spare2);
			SW.Write(ScatterCount);

			// Write the identifier
			SW.CommitDeferredField(IdentifierStringOffset);

			byte[] IdentifierOutput = Utilities.CreateASCIIZ(Identifier);
			SW.Write(IdentifierOutput);

			// Write the hashes
			SW.CommitDeferredField(HashOffset);
			SW.Write(Hashes);
		}
Exemple #25
0
		protected override void PackageData(WritingContext SW)
		{
			SW.Write(BlobFileOffset);
			SW.Write(BlobFileSize);
		}
Exemple #26
0
		/// <summary>
		/// Patches the the blob length and offset of the code signing blob in an existing file
		/// </summary>
		public void PatchPositionAndSize(WritingContext SW, UInt32 NewOffset, UInt32 NewLength)
		{
			Debug.Assert(StartingLoadOffset >= 0);

			if (Config.bCodeSignVerbose)
			{
				Console.WriteLine("ZZZZZZZZZZZZZZZZ");
				Console.WriteLine(" Blob offset from 0x{0:X} to 0x{1:X} (delta {2})", BlobFileOffset, NewOffset, (long)NewOffset - (long)BlobFileOffset);
				Console.WriteLine(" Blob size from 0x{0:X} to 0x{1:X} (delta {2})", BlobFileSize, NewLength, (long)NewLength - (long)BlobFileSize);
				Console.WriteLine("ZZZZZZZZZZZZZZZZ");
			}

			BlobFileOffset = NewOffset;
			BlobFileSize = NewLength;

			long PatchOffset = StartingLoadOffset + (2 * sizeof(UInt32)); // Command, CommandLength

			SW.PushPositionAndJump(PatchOffset);
			SW.Write(BlobFileOffset);
			SW.Write(BlobFileSize);
			SW.PopPosition();
		}
Exemple #27
0
		protected override void PackageData(WritingContext SW)
		{
			// Write the segment load command
			SW.WriteFixedASCII(SegmentName, 16);
			SW.WriteUInt(VirtualAddress, AddressSize);
			SW.WriteUInt(VirtualSize, AddressSize);

			// Offset to first segment
			//@TODO: These file offsets and file lengths aren't correct (compared to the original MachO's)
			OffsetFieldU32or64 FileOffset = SW.WriteDeferredOffsetFrom(0, AddressSize);
			LengthFieldU32or64 FileLength = SW.WriteDeferredLength(0, AddressSize);

			SW.Write(MaxProt);
			SW.Write(InitProt);
			SW.Write(Sections.Count);
			SW.Write(Flags);

			// Enqueue a job to commit the file offset to the first section
			SW.CurrentPhase.PendingWrites.Enqueue(delegate(WritingContext Context)
			{
				FileLength.Rebase(Context.Position);
				FileOffset.Commit(Context);
			});

			// Write the sections belonging to the segment
			foreach (MachSection Section in Sections)
			{
				Section.Write(SW);
			}

			// Enqueue a job to commit the length of data in all the sections
			SW.CurrentPhase.PendingWrites.Enqueue(delegate(WritingContext Context)
			{
				FileLength.Commit(Context);
			});
		}
Exemple #28
0
		protected void Write(WritingContext Context)
		{
			if (bIsFatBinary)
			{
				// Write the header
				Context.Write(Magic);
				Context.Write(NumArchs);

				Context.PushPosition();

				foreach (FatBinaryArch Arch in Archs)
				{
					Context.Write(Arch.CpuType);
					Context.Write(Arch.CpuSubType);
					Context.Write(Arch.Offset);
					Context.Write(Arch.Size);
					Context.Write(Arch.Align);
				}

				int FileIdx = 0;
				foreach (MachObjectFile MachFile in MachObjectFiles)
				{
					Archs[FileIdx].Offset = Convert.ToUInt32(Context.Position);
					MachFile.Write(Context);
					Archs[FileIdx].Size = Convert.ToUInt32(Context.Position) - Archs[FileIdx].Offset;
					FileIdx++;
				}

				Context.PopPosition();

				// Write updated header.
				foreach (FatBinaryArch Arch in Archs)
				{
					Context.Write(Arch.CpuType);
					Context.Write(Arch.CpuSubType);
					Context.Write(Arch.Offset);
					Context.Write(Arch.Size);
					Context.Write(Arch.Align);
				}
			}
			else
			{
				// Should only be one...
				MachObjectFiles[0].Write(Context);
			}
		}
Exemple #29
0
        /// <summary>
        /// Does the actual work of signing the application
        ///   Modifies the following files:
        ///	 Info.plist
        ///	 [Executable] (file name derived from CFBundleExecutable in the Info.plist, e.g., UDKGame)
        ///	 _CodeSignature/CodeResources
        ///	 [ResourceRules] (file name derived from CFBundleResourceSpecification, e.g., CustomResourceRules.plist)
        /// </summary>
        public void PerformSigning()
        {
            DateTime SigningTime = DateTime.Now;

            // Get the name of the executable file
            string CFBundleExecutable;
            if (!Info.GetString("CFBundleExecutable", out CFBundleExecutable))
            {
                throw new InvalidDataException("Info.plist must contain the key CFBundleExecutable");
            }

            // Get the name of the bundle
            string CFBundleIdentifier;
            if (!Info.GetString("CFBundleIdentifier", out CFBundleIdentifier))
            {
                throw new InvalidDataException("Info.plist must contain the key CFBundleIdentifier");
            }

            // Verify there is a resource rules file and make a dummy one if needed.
            // If it's missing, CreateCodeResourceDirectory can't proceed (the Info.plist is already written to disk at that point)
            if (!Info.HasKey("CFBundleResourceSpecification"))
            {
                // Couldn't find the key, create a dummy one
                string CFBundleResourceSpecification = "CustomResourceRules.plist";
                Info.SetString("CFBundleResourceSpecification", CFBundleResourceSpecification);

                Program.Warning("Info.plist was missing the key CFBundleResourceSpecification, creating a new resource rules file '{0}'.", CFBundleResourceSpecification);
            }

            // Save the Info.plist out
            byte[] RawInfoPList = Encoding.UTF8.GetBytes(Info.SaveToString());
            Info.SetReadOnly(true);
            FileSystem.WriteAllBytes("Info.plist", RawInfoPList);
            Program.Log(" ... Writing updated Info.plist");

            // Create the code resources file and load it
            byte[] ResourceDirBytes = CreateCodeResourcesDirectory(CFBundleExecutable);

            // Open the executable
            Program.Log("Opening source executable...");
            byte[] SourceExeData = FileSystem.ReadAllBytes(CFBundleExecutable);

            FatBinaryFile FatBinary = new FatBinaryFile();
            FatBinary.LoadFromBytes(SourceExeData);

            //@TODO: Verify it's an executable (not an object file, etc...)
            ulong CurrentStreamOffset = 0;
            byte[] FinalExeData = new byte[SourceExeData.Length + 1024 * 1024];
            int ArchIndex = 0;

            foreach (MachObjectFile Exe in FatBinary.MachObjectFiles)
            {
                Program.Log("... Processing one mach object (binary is {0})", FatBinary.bIsFatBinary ? "fat" : "thin");

                // Pad the memory stream with extra room to handle any possible growth in the code signing data
                int OverSize = 1024 * 1024;
                int ExeSize = (FatBinary.bIsFatBinary ? (int)FatBinary.Archs[ArchIndex].Size : SourceExeData.Length);
                MemoryStream OutputExeStream = new MemoryStream(ExeSize + OverSize);

                // Copy the data up to the executable into the final stream
                if (FatBinary.bIsFatBinary)
                {
                    if (ArchIndex == 0)
                    {
                        OutputExeStream.Seek(0, SeekOrigin.Begin);
                        OutputExeStream.Write(SourceExeData, (int)CurrentStreamOffset, (int)FatBinary.Archs[ArchIndex].Offset - (int)CurrentStreamOffset);
                        OutputExeStream.Seek(0, SeekOrigin.Begin);

                        byte[] HeaderData = OutputExeStream.ToArray();
                        HeaderData.CopyTo(FinalExeData, (long)CurrentStreamOffset);
                        CurrentStreamOffset += (ulong)HeaderData.Length;
                    }
                    else
                    {
                        byte[] ZeroData = new byte[(int)FatBinary.Archs[ArchIndex].Offset - (int)CurrentStreamOffset];
                        ZeroData.CopyTo(FinalExeData, (long)CurrentStreamOffset);
                        CurrentStreamOffset += (ulong)ZeroData.Length;
                    }
                }

                // Copy the executable into the stream
                int ExeOffset = (FatBinary.bIsFatBinary ? (int)FatBinary.Archs[ArchIndex].Offset : 0);
                OutputExeStream.Seek(0, SeekOrigin.Begin);
                OutputExeStream.Write(SourceExeData, ExeOffset, ExeSize);
                OutputExeStream.Seek(0, SeekOrigin.Begin);
                long Length = OutputExeStream.Length;

                // Find out if there was an existing code sign blob and find the linkedit segment command
                MachLoadCommandCodeSignature CodeSigningBlobLC = null;
                MachLoadCommandSegment LinkEditSegmentLC = null;
                foreach (MachLoadCommand Command in Exe.Commands)
                {
                    if (CodeSigningBlobLC == null)
                    {
                        CodeSigningBlobLC = Command as MachLoadCommandCodeSignature;
                    }

                    if (LinkEditSegmentLC == null)
                    {
                        LinkEditSegmentLC = Command as MachLoadCommandSegment;
                        if (LinkEditSegmentLC.SegmentName != "__LINKEDIT")
                        {
                            LinkEditSegmentLC = null;
                        }
                    }
                }

                if (LinkEditSegmentLC == null)
                {
                    throw new InvalidDataException("Did not find a Mach segment load command for the __LINKEDIT segment");
                }

                // If the existing code signing blob command is missing, make sure there is enough space to add it
                // Insert the code signing blob if it isn't present
                //@TODO: Insert the code signing blob if it isn't present
                if (CodeSigningBlobLC == null)
                {
                    throw new InvalidDataException("Did not find a Code Signing LC.  Injecting one into a fresh executable is not currently supported.");
                }

                // Verify that the code signing blob is at the end of the linkedit segment (and thus can be expanded if needed)
                if ((CodeSigningBlobLC.BlobFileOffset + CodeSigningBlobLC.BlobFileSize) != (LinkEditSegmentLC.FileOffset + LinkEditSegmentLC.FileSize))
                {
                    throw new InvalidDataException("Code Signing LC was present but not at the end of the __LINKEDIT segment, unable to replace it");
                }

                int SignedFileLength = (int)CodeSigningBlobLC.BlobFileOffset;

                // Create the code directory blob
                CodeDirectoryBlob FinalCodeDirectoryBlob = CodeDirectoryBlob.Create(CFBundleIdentifier, SignedFileLength);

                // Create the entitlements blob
                string EntitlementsText = BuildEntitlementString(CFBundleIdentifier);
                EntitlementsBlob FinalEntitlementsBlob = EntitlementsBlob.Create(EntitlementsText);

                // Create or preserve the requirements blob
                RequirementsBlob FinalRequirementsBlob = null;
                if ((CodeSigningBlobLC != null) && Config.bMaintainExistingRequirementsWhenCodeSigning)
                {
                    RequirementsBlob OldRequirements = CodeSigningBlobLC.Payload.GetBlobByMagic(AbstractBlob.CSMAGIC_REQUIREMENTS_TABLE) as RequirementsBlob;
                    FinalRequirementsBlob = OldRequirements;
                }

                if (FinalRequirementsBlob == null)
                {
                    FinalRequirementsBlob = RequirementsBlob.CreateEmpty();
                }

                // Create the code signature blob (which actually signs the code directory)
                CodeDirectorySignatureBlob CodeSignatureBlob = CodeDirectorySignatureBlob.Create();

                // Create the code signature superblob (which contains all of the other signature-related blobs)
                CodeSigningTableBlob CodeSignPayload = CodeSigningTableBlob.Create();
                CodeSignPayload.Add(0x00000, FinalCodeDirectoryBlob);
                CodeSignPayload.Add(0x00002, FinalRequirementsBlob);
                CodeSignPayload.Add(0x00005, FinalEntitlementsBlob);
                CodeSignPayload.Add(0x10000, CodeSignatureBlob);

                // The ordering of the following steps (and doing the signature twice below) must be preserved.
                // The reason is there are some chicken-and-egg issues here:
                //   The code directory stores a hash of the header, but
                //   The header stores the size of the __LINKEDIT section, which is where the signature blobs go, but
                //   The CMS signature blob signs the code directory
                //
                // So, we need to know the size of a signature blob in order to write a header that is itself hashed
                // and signed by the signature blob

                // Do an initial signature just to get the size
                Program.Log("... Initial signature step ({0:0.00} s elapsed so far)", (DateTime.Now - SigningTime).TotalSeconds);
                CodeSignatureBlob.SignCodeDirectory(SigningCert, SigningTime, FinalCodeDirectoryBlob);

                // Compute the size of everything, and push it into the EXE header
                byte[] DummyPayload = CodeSignPayload.GetBlobBytes();

                // Adjust the header and load command to have the correct size for the code sign blob
                WritingContext OutputExeContext = new WritingContext(new BinaryWriter(OutputExeStream));

                long BlobLength = DummyPayload.Length;

                long NonCodeSigSize = (long)LinkEditSegmentLC.FileSize - CodeSigningBlobLC.BlobFileSize;
                long BlobStartPosition = NonCodeSigSize + (long)LinkEditSegmentLC.FileOffset;

                LinkEditSegmentLC.PatchFileLength(OutputExeContext, (uint)(NonCodeSigSize + BlobLength));
                CodeSigningBlobLC.PatchPositionAndSize(OutputExeContext, (uint)BlobStartPosition, (uint)BlobLength);

                // Now that the executable loader command has been inserted and the appropriate section modified, compute all the hashes
                Program.Log("... Computing hashes ({0:0.00} s elapsed so far)", (DateTime.Now - SigningTime).TotalSeconds);
                OutputExeContext.Flush();

                // Fill out the special hashes
                FinalCodeDirectoryBlob.GenerateSpecialSlotHash(CodeDirectoryBlob.cdInfoSlot, RawInfoPList);
                FinalCodeDirectoryBlob.GenerateSpecialSlotHash(CodeDirectoryBlob.cdRequirementsSlot, FinalRequirementsBlob.GetBlobBytes());
                FinalCodeDirectoryBlob.GenerateSpecialSlotHash(CodeDirectoryBlob.cdResourceDirSlot, ResourceDirBytes);
                FinalCodeDirectoryBlob.GenerateSpecialSlotHash(CodeDirectoryBlob.cdApplicationSlot);
                FinalCodeDirectoryBlob.GenerateSpecialSlotHash(CodeDirectoryBlob.cdEntitlementSlot, FinalEntitlementsBlob.GetBlobBytes());

                // Fill out the regular hashes
                FinalCodeDirectoryBlob.ComputeImageHashes(OutputExeStream.ToArray());

                // And compute the final signature
                Program.Log("... Final signature step ({0:0.00} s elapsed so far)", (DateTime.Now - SigningTime).TotalSeconds);
                CodeSignatureBlob.SignCodeDirectory(SigningCert, SigningTime, FinalCodeDirectoryBlob);

                // Generate the signing blob and place it in the output (verifying it didn't change in size)
                byte[] FinalPayload = CodeSignPayload.GetBlobBytes();

                if (DummyPayload.Length != FinalPayload.Length)
                {
                    throw new InvalidDataException("CMS signature blob changed size between practice run and final run, unable to create useful code signing data");
                }

                OutputExeContext.PushPositionAndJump(BlobStartPosition);
                OutputExeContext.Write(FinalPayload);
                OutputExeContext.PopPosition();

                // Truncate the data so the __LINKEDIT section extends right to the end
                Program.Log("... Committing all edits ({0:0.00} s elapsed so far)", (DateTime.Now - SigningTime).TotalSeconds);
                OutputExeContext.CompleteWritingAndClose();

                Program.Log("... Truncating/copying final binary", DateTime.Now - SigningTime);
                ulong DesiredExecutableLength = LinkEditSegmentLC.FileSize + LinkEditSegmentLC.FileOffset;

                if ((ulong)Length < DesiredExecutableLength)
                {
                    throw new InvalidDataException("Data written is smaller than expected, unable to finish signing process");
                }
                byte[] Data = OutputExeStream.ToArray();
                Data.CopyTo(FinalExeData, (long)CurrentStreamOffset);
                CurrentStreamOffset += DesiredExecutableLength;

                // update the header if it is a fat binary
                if (FatBinary.bIsFatBinary)
                {
                    FatBinary.Archs[ArchIndex].Size = (uint)DesiredExecutableLength;
                }

                // increment the architecture index
                ArchIndex++;
            }

            // re-write the header
            FatBinary.WriteHeader(ref FinalExeData, 0);

            // resize to the finale size
            Array.Resize(ref FinalExeData, (int)CurrentStreamOffset); //@todo: Extend the file system interface so we don't have to copy 20 MB just to truncate a few hundred bytes

            // Save the patched and signed executable
            Program.Log("Saving signed executable... ({0:0.00} s elapsed so far)", (DateTime.Now - SigningTime).TotalSeconds);
            FileSystem.WriteAllBytes(CFBundleExecutable, FinalExeData);

            Program.Log("Finished code signing, which took {0:0.00} s", (DateTime.Now - SigningTime).TotalSeconds);
        }
			public override void WriteData(WritingContext SW)
			{
				base.WriteData(SW);
				SW.Write(BundleIdentifier.Length);								// bundle identifier length
				SW.WriteFixedASCII(BundleIdentifier, BundleIdentifier.Length);	// bundle identifier string
				int Count = 4 - BundleIdentifier.Length % 4;					// may need to pad to alignment of 4 bytes
				if (Count > 0 && Count < 4)
					SW.WriteZeros(Count);
			}