Skip to content

Commit

Permalink
Additional load commands
Browse files Browse the repository at this point in the history
- MachEncryptionInfo
- MachRunPath
- MachSourceVersion
- MachUuid
  • Loading branch information
qmfrederik committed Nov 23, 2022
1 parent ef829f2 commit 2a0b528
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 8 deletions.
6 changes: 6 additions & 0 deletions CodeSign.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Melanzana.CodeSign", "Melan
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Melanzana.CodeSign.Tests", "Melanzana.CodeSign.Tests\Melanzana.CodeSign.Tests.csproj", "{30D19045-7A46-4993-B018-1F60397A741D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Melanzana.Streams", "Melanzana.Streams\Melanzana.Streams.csproj", "{DF6E2C80-90A8-4C69-AD60-E494A6D4267B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -39,6 +41,10 @@ Global
{30D19045-7A46-4993-B018-1F60397A741D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30D19045-7A46-4993-B018-1F60397A741D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30D19045-7A46-4993-B018-1F60397A741D}.Release|Any CPU.Build.0 = Release|Any CPU
{DF6E2C80-90A8-4C69-AD60-E494A6D4267B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DF6E2C80-90A8-4C69-AD60-E494A6D4267B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DF6E2C80-90A8-4C69-AD60-E494A6D4267B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DF6E2C80-90A8-4C69-AD60-E494A6D4267B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
10 changes: 10 additions & 0 deletions Melanzana.MachO/LoadCommands/MachEncryptionInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Melanzana.MachO
{
[GenerateReaderWriter]
public partial class MachEncryptionInfo : MachLoadCommand
{
public uint CryptOffset;
public uint CryptSize;
public uint CryptId;
}
}
7 changes: 7 additions & 0 deletions Melanzana.MachO/LoadCommands/MachRunPath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Melanzana.MachO
{
public class MachRunPath : MachLoadCommand
{
public string? RunPath { get; set; }
}
}
11 changes: 11 additions & 0 deletions Melanzana.MachO/LoadCommands/MachSourceVersion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Melanzana.MachO
{
[GenerateReaderWriter]
public partial class MachSourceVersion : MachLoadCommand
{
/// <summary>
/// A.B.C.D.E packed as a24.b10.c10.d10.e10.
/// </summary>
public ulong Version { get; set; }
}
}
7 changes: 7 additions & 0 deletions Melanzana.MachO/LoadCommands/MachUuid.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Melanzana.MachO
{
public partial class MachUuid : MachLoadCommand
{
public Guid Uuid { get; set; }
}
}
48 changes: 41 additions & 7 deletions Melanzana.MachO/MachReader.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using Melanzana.MachO.BinaryFormat;
using Melanzana.Streams;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Melanzana.MachO.BinaryFormat;
using Melanzana.Streams;

namespace Melanzana.MachO
{
Expand All @@ -13,7 +12,7 @@ private static MachSegment ReadSegment(ReadOnlySpan<byte> loadCommandPtr, MachOb
{
var segmentHeader = SegmentHeader.Read(loadCommandPtr.Slice(LoadCommandHeader.BinarySize), objectFile.IsLittleEndian, out var _);

var machSegment = segmentHeader.NumberOfSections == 0 && segmentHeader.FileSize != 0 ?
var machSegment = segmentHeader.NumberOfSections == 0 && segmentHeader.FileSize != 0 ?
new MachSegment(objectFile, segmentHeader.Name, stream.Slice(segmentHeader.FileOffset, segmentHeader.FileSize)) :
new MachSegment(objectFile, segmentHeader.Name);
machSegment.FileOffset = segmentHeader.FileOffset;
Expand Down Expand Up @@ -71,7 +70,7 @@ private static MachSegment ReadSegment64(ReadOnlySpan<byte> loadCommandPtr, Mach
{
var segmentHeader = Segment64Header.Read(loadCommandPtr.Slice(LoadCommandHeader.BinarySize), objectFile.IsLittleEndian, out var _);

var machSegment = segmentHeader.NumberOfSections == 0 && segmentHeader.FileSize != 0 ?
var machSegment = segmentHeader.NumberOfSections == 0 && segmentHeader.FileSize != 0 ?
new MachSegment(objectFile, segmentHeader.Name, stream.Slice((long)segmentHeader.FileOffset, (long)segmentHeader.FileSize)) :
new MachSegment(objectFile, segmentHeader.Name);
machSegment.FileOffset = segmentHeader.FileOffset;
Expand Down Expand Up @@ -258,6 +257,37 @@ private static MachTwoLevelHints ReadTwoLevelHints(ReadOnlySpan<byte> loadComman
twoLevelHintsHeader.NumberOfHints * sizeof(uint)));
}

private static MachUuid ReadUuid(ReadOnlySpan<byte> loadCommandPtr)
{
return new MachUuid()
{
Uuid = new Guid(loadCommandPtr.Slice(LoadCommandHeader.BinarySize, 16)),
};
}

private static MachSourceVersion ReadSourceVersion(ReadOnlySpan<byte> loadCommandPtr, bool isLittleEndian)
=> MachSourceVersion.Read(loadCommandPtr.Slice(LoadCommandHeader.BinarySize), isLittleEndian, out int _);

private static MachEncryptionInfo ReadEncryptionInfo64(ReadOnlySpan<byte> loadCommandPtr, bool isLittleEndian)
=> MachEncryptionInfo.Read(loadCommandPtr.Slice(LoadCommandHeader.BinarySize), isLittleEndian, out int _);

private static MachRunPath ReadRunPath(ReadOnlySpan<byte> loadCommandPtr, bool isLittleEndian)
{
var offset =
isLittleEndian
? BinaryPrimitives.ReadUInt32LittleEndian(loadCommandPtr.Slice(LoadCommandHeader.BinarySize))
: BinaryPrimitives.ReadUInt32BigEndian(loadCommandPtr.Slice(LoadCommandHeader.BinarySize));

var nameSlice = loadCommandPtr.Slice((int)offset);
int zeroIndex = nameSlice.IndexOf((byte)0);
string name = zeroIndex >= 0 ? Encoding.UTF8.GetString(nameSlice.Slice(0, zeroIndex)) : Encoding.UTF8.GetString(nameSlice);

return new MachRunPath()
{
RunPath = name,
};
}

private static MachObjectFile ReadSingle(FatArchHeader? fatArchHeader, MachMagic magic, Stream stream)
{
Span<byte> headerBuffer = stackalloc byte[Math.Max(MachHeader.BinarySize, MachHeader64.BinarySize)];
Expand Down Expand Up @@ -329,11 +359,15 @@ private static MachObjectFile ReadSingle(FatArchHeader? fatArchHeader, MachMagic
MachLoadCommandType.BuildVersion => ReadBuildVersion(loadCommandPtr, isLittleEndian),
MachLoadCommandType.SymbolTable => ReadSymbolTable(loadCommandPtr, objectFile, stream),
MachLoadCommandType.DynamicLinkEditSymbolTable => ReadDynamicLinkEditSymbolTable(loadCommandPtr, isLittleEndian),
MachLoadCommandType.DyldInfo => ReadDyldInfo(loadCommandHeader.CommandType, loadCommandPtr, objectFile, stream),
MachLoadCommandType.DyldInfo => ReadDyldInfo(loadCommandHeader.CommandType, loadCommandPtr, objectFile, stream),
MachLoadCommandType.DyldInfoOnly => ReadDyldInfo(loadCommandHeader.CommandType, loadCommandPtr, objectFile, stream),
MachLoadCommandType.TowLevelHints => ReadTwoLevelHints(loadCommandPtr, objectFile, stream),
MachLoadCommandType.Uuid => ReadUuid(loadCommandPtr),
MachLoadCommandType.SourceVersion => ReadSourceVersion(loadCommandPtr, objectFile.IsLittleEndian),
MachLoadCommandType.EncryptionInfo64 => ReadEncryptionInfo64(loadCommandPtr, objectFile.IsLittleEndian),
MachLoadCommandType.Rpath => ReadRunPath(loadCommandPtr, objectFile.IsLittleEndian),
_ => new MachCustomLoadCommand(loadCommandHeader.CommandType, loadCommandPtr.Slice(LoadCommandHeader.BinarySize, (int)loadCommandHeader.CommandSize - LoadCommandHeader.BinarySize).ToArray()),
});
}); ;
loadCommandPtr = loadCommandPtr.Slice((int)loadCommandHeader.CommandSize);
}

Expand Down
71 changes: 70 additions & 1 deletion Melanzana.MachO/MachWriter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Buffers.Binary;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Text;
Expand Down Expand Up @@ -301,6 +302,69 @@ private static void WriteDyldInfoCommand(Stream stream, MachLoadCommandType comm
stream.Write(dyldInfoHeaderBuffer);
}

private static void WriteUuid(Stream stream, MachUuid uuid, bool isLittleEndian)
{
WriteLoadCommandHeader(
stream,
MachLoadCommandType.Uuid,
LoadCommandHeader.BinarySize + 16,
isLittleEndian);

Span<byte> uuidBuffer = stackalloc byte[16];
uuid.Uuid.TryWriteBytes(uuidBuffer);
stream.Write(uuidBuffer);
}

private static void WriteSourceVersion(Stream stream, MachSourceVersion sourceVersion, bool isLittleEndian)
{
WriteLoadCommandHeader(
stream,
MachLoadCommandType.SourceVersion,
LoadCommandHeader.BinarySize + MachSourceVersion.BinarySize,
isLittleEndian);

Span<byte> sourceVersionBuffer = stackalloc byte[MachSourceVersion.BinarySize];
sourceVersion.Write(sourceVersionBuffer, isLittleEndian, out var _);
stream.Write(sourceVersionBuffer);
}

private static void WriteEncryptionInfo64(Stream stream, MachEncryptionInfo encryptionInfo, bool isLittleEndian)
{
WriteLoadCommandHeader(
stream,
MachLoadCommandType.EncryptionInfo64,
LoadCommandHeader.BinarySize + MachEncryptionInfo.BinarySize,
isLittleEndian);

Span<byte> encryptionInfoBuffer = stackalloc byte[MachEncryptionInfo.BinarySize];
encryptionInfo.Write(encryptionInfoBuffer, isLittleEndian, out var _);
stream.Write(encryptionInfoBuffer);
}

private static void WriteRunPath(Stream stream, MachRunPath runPath, bool isLittleEndian, bool is64Bit)
{
var valueLength = 4 + Encoding.UTF8.GetByteCount(runPath.RunPath) + 1;
int commandSize = AlignedSize(
LoadCommandHeader.BinarySize + valueLength,
is64Bit);

WriteLoadCommandHeader(
stream,
MachLoadCommandType.Rpath,
commandSize,
isLittleEndian);

Span<byte> buffer = stackalloc byte[valueLength];
// NameOffset
BinaryPrimitives.WriteInt32LittleEndian(buffer, LoadCommandHeader.BinarySize + 4);
Encoding.UTF8.GetBytes(runPath.RunPath, buffer.Slice(4));
buffer[buffer.Length - 1] = 0;

// The name is always written with terminating `\0` and aligned to platform
// pointer size.
stream.WritePadding(commandSize - valueLength);
}

private static void WriteTwoLevelHintsCommand(Stream stream, MachTwoLevelHints twoLevelHints, bool isLittleEndian)
{
WriteLoadCommandHeader(
Expand Down Expand Up @@ -363,8 +427,13 @@ public static void Write(Stream stream, MachObjectFile objectFile)
WriteLoadCommandHeader(loadCommandsStream, customLoadCommand.Type, customLoadCommand.Data.Length + LoadCommandHeader.BinarySize, isLittleEndian);
loadCommandsStream.Write(customLoadCommand.Data);
break;
case MachUuid uuid: WriteUuid(loadCommandsStream, uuid, isLittleEndian); break;
case MachSourceVersion sourceVersion: WriteSourceVersion(loadCommandsStream, sourceVersion, isLittleEndian); break;
case MachEncryptionInfo encryptionInfo: WriteEncryptionInfo64(loadCommandsStream, encryptionInfo, isLittleEndian); break;
case MachRunPath runPath: WriteRunPath(loadCommandsStream, runPath, isLittleEndian, objectFile.Is64Bit); break;

default:
Debug.Fail("Unknown load command");
Debug.Fail($"Unknown load command {loadCommand.GetType().Name}");
break;
}
}
Expand Down

0 comments on commit 2a0b528

Please sign in to comment.