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); }
/// <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(); }
protected override void PackageData(WritingContext SW) { SW.Write(SymbolTableOffset); SW.Write(SymbolCount); SW.Write(StringTableOffset); SW.Write(StringTableSize); }
protected abstract void PackageData(WritingContext SW);
protected override void PackageData(WritingContext SW) { SW.Write(MyCommandData, 0, MyCommandData.Length); }
public DeferredFieldU32or64(WritingContext Context, long Base, Bits.Num Address) { AnchorPoint = Base; WritePoint = Context.Position; AddressSize = Address; Context.Position += Bits.Bytes(AddressSize); }
public OffsetFieldU32or64(WritingContext Context, long BaseOffset, Bits.Num Address) : base(Context, BaseOffset, Address) { }
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(); }
public void Write(WritingContext SW) { SW.Write(MyMagic); LengthFieldU32or64 Length = SW.WriteDeferredLength(4, Bits.Num._32); PackageData(SW); SW.CommitDeferredField(Length); }
protected override void PackageData(WritingContext SW) { SW.Write(EncryptedFileOffset); SW.Write(EncryptedFileSize); SW.Write(EncryptionMode); }
// 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 }
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); } }
/// <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(); }
public void WriteToFile(string Filename) { BinaryWriter SW = new BinaryWriter(File.OpenWrite(Filename)); WritingContext Context = new WritingContext(SW); Write(Context); SW.Close(); }
protected override void PackageData(WritingContext SW) { SW.Write(MyData); }
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(); }
protected override void PackageData(WritingContext SW) { Table.Write(SW); }
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); }
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(); }
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); }
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); }
protected override void PackageData(WritingContext SW) { SW.Write(BlobFileOffset); SW.Write(BlobFileSize); }
/// <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(); }
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); }); }
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); } }
/// <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); }