in net/JetBrains.FormatRipper/src/MachO/MachOSignatureInjector.cs [58:140]
private static unsafe void ProcessFatMachO(Stream sourceStream, Stream outputStream, MH fatMagic, IMachOSignatureTransferData signatureTransferData)
{
var isFatLittleEndian = fatMagic is MH.FAT_MAGIC or MH.FAT_MAGIC_64;
var needSwap = BitConverter.IsLittleEndian != isFatLittleEndian;
uint GetU4(uint v) => needSwap ? MemoryUtil.SwapU4(v) : v;
ulong GetU8(ulong v) => needSwap ? MemoryUtil.SwapU8(v) : v;
fat_header fh;
StreamUtil.ReadBytes(sourceStream, (byte*)&fh, sizeof(fat_header));
var nFatArch = GetU4(fh.nfat_arch);
if (signatureTransferData.SectionSignatures.Length != nFatArch)
throw new SignatureInjectionException($"Cannot transfer signatures: source and destination files have different number of sections. The source file has {signatureTransferData.SectionSignatures.Length} section(s) and the destination file has {nFatArch} section(s).");
uint rawFatMagic = MemoryUtil.GetLeU4((uint)fatMagic);
StreamUtil.WriteBytes(outputStream, (byte*)&rawFatMagic, sizeof(uint));
StreamUtil.WriteBytes(outputStream, (byte*)&fh, sizeof(fat_header));
long fatNodesOffset = sourceStream.Position;
if (fatMagic is MH.FAT_CIGAM_64 or MH.FAT_MAGIC_64)
{
var fatNodes = new fat_arch_64[checked((int)nFatArch)];
fixed (fat_arch_64* ptr = fatNodes)
{
int fatNodesSize = checked((int)nFatArch * sizeof(fat_arch_64));
StreamUtil.ReadBytes(sourceStream, (byte*)ptr, fatNodesSize);
StreamUtil.WriteBytes(outputStream, (byte*)ptr, fatNodesSize);
for (var n = 0; n < nFatArch; n++)
{
var processedSection = ProcessSection(
sourceStream,
outputStream,
new MachOSectionInfo()
{
SectionOffset = checked((long)GetU8(fatNodes[n].offset)),
SectionSize = checked((long)GetU8(fatNodes[n].size)),
Alignment = (int)GetU4(fatNodes[n].align)
},
signatureTransferData.SectionSignatures[n]);
fatNodes[n].offset = GetU8((ulong)processedSection.SectionOffset);
fatNodes[n].size = GetU8((ulong)processedSection.SectionSize);
}
outputStream.Seek(fatNodesOffset, SeekOrigin.Begin);
StreamUtil.WriteBytes(outputStream, (byte*)ptr, fatNodesSize);
}
}
else
{
var fatNodes = new fat_arch[checked((int)nFatArch)];
fixed (fat_arch* ptr = fatNodes)
{
int fatNodesSize = checked((int)nFatArch * sizeof(fat_arch));
StreamUtil.ReadBytes(sourceStream, (byte*)ptr, fatNodesSize);
StreamUtil.WriteBytes(outputStream, (byte*)ptr, fatNodesSize);
for (var n = 0; n < nFatArch; n++)
{
var processedSection = ProcessSection(
sourceStream,
outputStream,
new MachOSectionInfo()
{
SectionOffset = GetU4(fatNodes[n].offset),
SectionSize = GetU4(fatNodes[n].size),
Alignment = (int)GetU4(fatNodes[n].align)
},
signatureTransferData.SectionSignatures[n]);
fatNodes[n].offset = GetU4(checked((uint)processedSection.SectionOffset));
fatNodes[n].size = GetU4(checked((uint)processedSection.SectionSize));
}
// Update fat nodes with new offsets and sizes
outputStream.Seek(fatNodesOffset, SeekOrigin.Begin);
StreamUtil.WriteBytes(outputStream, (byte*)ptr, fatNodesSize);
}
}
}