private static unsafe long TransferSectionSignature()

in net/JetBrains.FormatRipper/src/MachO/MachOSignatureInjector.cs [179:349]


  private static unsafe long TransferSectionSignature(Stream sourceStream, StreamRange sourceStreamRange, Stream outputStream, MH magic, IMachOSectionSignatureTransferData? sectionSignature)
  {
    if (sectionSignature == null)
      throw new SignatureInjectionException($"Cannot transfer the signature for section: it is not signed in the original file");

    long outputStreamInitialPosition = outputStream.Position;
    long PositionFromStart() => outputStream.Position - outputStreamInitialPosition;

    var isLittleEndian = magic is MH.MH_MAGIC or MH.MH_MAGIC_64;
    var needSwap = BitConverter.IsLittleEndian != isLittleEndian;

    uint GetU4(uint v) => needSwap ? MemoryUtil.SwapU4(v) : v;
    ulong GetU8(ulong v) => needSwap ? MemoryUtil.SwapU8(v) : v;

    uint rawMagic = MemoryUtil.GetLeU4((uint)magic);
    StreamUtil.WriteBytes(outputStream, (byte*)&rawMagic, sizeof(uint));

    uint ncmds;
    uint sizeofcmds;
    bool signatureLoadCommandMissing = false;

    if (magic is MH.MH_MAGIC_64 or MH.MH_CIGAM_64)
    {
      mach_header_64 mh;
      StreamUtil.ReadBytes(sourceStream, (byte*)&mh, sizeof(mach_header_64));

      ncmds = GetU4(mh.ncmds);
      sizeofcmds = GetU4(mh.sizeofcmds);

      if (ncmds == sectionSignature.NumberOfLoadCommands - 1)
      {
        mh.ncmds = GetU4(sectionSignature.NumberOfLoadCommands);
        mh.sizeofcmds = GetU4(sectionSignature.SizeOfLoadCommands);
        signatureLoadCommandMissing = true;
      }
      else if (ncmds != sectionSignature.NumberOfLoadCommands)
      {
        throw new SignatureInjectionException($"Target file has wrong number of load commands. Expected {sectionSignature.NumberOfLoadCommands} or {sectionSignature.NumberOfLoadCommands - 1}, but found {ncmds}");
      }

      StreamUtil.WriteBytes(outputStream, (byte*)&mh, sizeof(mach_header_64));
    }
    else if (magic is MH.MH_MAGIC or MH.MH_CIGAM)
    {
      mach_header mh;
      StreamUtil.ReadBytes(sourceStream, (byte*)&mh, sizeof(mach_header));

      ncmds = GetU4(mh.ncmds);
      sizeofcmds = GetU4(mh.sizeofcmds);

      if (ncmds == sectionSignature.NumberOfLoadCommands - 1)
      {
        mh.ncmds = GetU4(sectionSignature.NumberOfLoadCommands);
        mh.sizeofcmds = GetU4(sectionSignature.SizeOfLoadCommands);
        signatureLoadCommandMissing = true;
      }
      else if (ncmds != sectionSignature.NumberOfLoadCommands)
      {
        throw new SignatureInjectionException($"Target file has wrong number of load commands. Expected {sectionSignature.NumberOfLoadCommands} or {sectionSignature.NumberOfLoadCommands - 1}, but found {ncmds}");
      }

      StreamUtil.WriteBytes(outputStream, (byte*)&mh, sizeof(mach_header));
    }
    else
    {
      throw new FormatException($"Invalid Mach-O magic: {magic.ToString("X")}");
    }

    uint oldSignatureOffset = 0;
    uint commandNumber = 0;
    fixed (byte* buf = StreamUtil.ReadBytes(sourceStream, checked((int)sizeofcmds)))
    {
      for (var cmdPtr = buf; commandNumber++ < ncmds;)
      {
        load_command lc;
        MemoryUtil.CopyBytes(cmdPtr, (byte*)&lc, sizeof(load_command));
        uint cmdSize = GetU4(lc.cmdsize);
        var payloadLcPtr = cmdPtr + sizeof(load_command);
        var commandType = (LC)GetU4(lc.cmd);

        if (commandNumber == sectionSignature.LastLinkeditCommandNumber)
        {
          if (commandType == LC.LC_SEGMENT_64)
          {
            segment_command_64 sc;
            MemoryUtil.CopyBytes(payloadLcPtr, (byte*)&sc, sizeof(segment_command_64));
            sc.vmsize = GetU8(sectionSignature.LastLinkeditVmSize64);
            sc.filesize = GetU8(sectionSignature.LastLinkeditFileSize64);

            MemoryUtil.CopyBytes((byte*)&sc, payloadLcPtr, sizeof(segment_command_64));
          }
          else if (commandType == LC.LC_SEGMENT)
          {
            segment_command sc;
            MemoryUtil.CopyBytes(payloadLcPtr, (byte*)&sc, sizeof(segment_command));
            sc.vmsize = GetU4(sectionSignature.LastLinkeditVmSize32);
            sc.filesize = GetU4(sectionSignature.LastLinkeditFileSize32);

            MemoryUtil.CopyBytes((byte*)&sc, payloadLcPtr, sizeof(segment_command));
          }
          else
          {
            throw new SignatureInjectionException($"Error injecting signature. Load command number {sectionSignature.LastLinkeditCommandNumber} was supposed to be either LC_SEGMENT or LC_SEGMENT_64, but {commandType.ToString()} was found");
          }
        }

        if (commandType == LC.LC_CODE_SIGNATURE)
        {
          linkedit_data_command ldc;
          MemoryUtil.CopyBytes(payloadLcPtr, (byte*)&ldc, sizeof(linkedit_data_command));

          oldSignatureOffset = GetU4(ldc.dataoff);

          ldc.dataoff = GetU4(sectionSignature.LinkEditDataOffset);
          ldc.datasize = GetU4(sectionSignature.LinkEditDataSize);

          MemoryUtil.CopyBytes((byte*)&ldc, payloadLcPtr, sizeof(linkedit_data_command));
        }

        cmdPtr += cmdSize;
      }

      StreamUtil.WriteBytes(outputStream, buf, checked((int)sizeofcmds));
    }

    if (signatureLoadCommandMissing)
    {
      load_command signatureLc = new load_command()
      {
        cmd = GetU4((uint)LC.LC_CODE_SIGNATURE),
        cmdsize = GetU4(sectionSignature.LcCodeSignatureSize),
      };

      StreamUtil.WriteBytes(outputStream, (byte*)&signatureLc, sizeof(load_command));

      linkedit_data_command signatureLdc = new linkedit_data_command()
      {
        dataoff = GetU4(sectionSignature.LinkEditDataOffset),
        datasize = GetU4(sectionSignature.LinkEditDataSize),
      };

      StreamUtil.WriteBytes(outputStream, (byte*)&signatureLdc, sizeof(linkedit_data_command));

      sourceStream.Seek(sizeof(load_command) + sizeof(linkedit_data_command), SeekOrigin.Current);
    }

    long end = oldSignatureOffset != 0 ? sourceStreamRange.Position + oldSignatureOffset : sourceStreamRange.Position + sourceStreamRange.Size;

    StreamUtil.CopyBytes(sourceStream, outputStream, end - sourceStream.Position);

    if (PositionFromStart() < sectionSignature.LinkEditDataOffset)
    {
      long paddingBytes = sectionSignature.LinkEditDataOffset - PositionFromStart();
      if (paddingBytes > MaxPaddingBytes)
        throw new SignatureInjectionException($"Too many padding bytes required between payload and signature. Expected no more than {MaxPaddingBytes} bytes, but required {paddingBytes} bytes. Seems that the donor and the acceptor files are incompatible.");

      byte[] padding = new byte[paddingBytes];
      outputStream.Write(padding, 0, padding.Length);
    }

    if (PositionFromStart() == sectionSignature.LinkEditDataOffset)
    {
      outputStream.Write(sectionSignature.SignatureBlob, 0, sectionSignature.SignatureBlob.Length);
    }
    else
    {
      throw new SignatureInjectionException($"Failed to inject signature due to invalid output stream position. Expected {sectionSignature.LinkEditDataOffset}, got {PositionFromStart()}");
    }

    return PositionFromStart();
  }