fix(macos): add ad-hoc code signature to generated Mach-O
Unsigned Mach-O executables are killed by the macOS kernel (SIGKILL) on macOS 11+, especially Apple Silicon. paze-compiled binaries like time.pe→time could not run at all. Add LC_CODE_SIGNATURE load command + SuperBlob/CodeDirectory (SHA-256, ad-hoc, no certificate needed) to both MachOWriter (x86-64) and MachOWriterArm64 (AArch64). The signature covers all code pages (4096-byte) from file start to the signature blob. New file: MachOCodeSignature.cs — shared signature generator.
这个提交包含在:
@@ -0,0 +1,94 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PazeE.Compiler.Binary;
|
||||
|
||||
/// <summary>生成 macOS ad-hoc 代码签名(SuperBlob + CodeDirectory,SHA-256)。
|
||||
/// macOS 11+(尤其 Apple Silicon)要求所有可执行文件至少有 ad-hoc 签名,否则内核 SIGKILL。
|
||||
/// 代码签名 blob 使用大端字节序(CSBlob 规范),与 Mach-O 主体的小端序不同。</summary>
|
||||
internal static class MachOCodeSignature
|
||||
{
|
||||
private const uint CSMAGIC_EMBEDDED_SIGNATURE = 0xFade0CC0; // SuperBlob
|
||||
private const uint CSMAGIC_CODEDIRECTORY = 0xFade0C02; // CodeDirectory
|
||||
private const uint CSSLOT_CODEDIRECTORY = 0; // blob index type
|
||||
private const int CS_PAGE_SIZE = 4096;
|
||||
private const byte CS_PAGE_SHIFT = 12; // log2(4096)
|
||||
private const byte CS_HASHTYPE_SHA256 = 2;
|
||||
private const byte CS_HASHSIZE_SHA256 = 32;
|
||||
private const string DefaultIdentifier = "paze-compiled";
|
||||
|
||||
// CodeDirectory 固定部分(version 0x20001,无 scatterOffset/teamOffset)
|
||||
private const int CdFixedLen = 44;
|
||||
// SuperBlob 头(12) + 1 个 BlobIndex(8)
|
||||
private const int SbHeaderLen = 12 + 8;
|
||||
|
||||
/// <summary>计算代码签名 blob 的确切大小(字节),用于在写入头之前确定 __LINKEDIT 段尺寸。</summary>
|
||||
public static int ComputeBlobSize(int codeLimit, string? identifier = null)
|
||||
{
|
||||
int identLen = Encoding.ASCII.GetBytes((identifier ?? DefaultIdentifier) + "\0").Length;
|
||||
int nCodeSlots = (codeLimit + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE;
|
||||
int cdLength = CdFixedLen + identLen + nCodeSlots * CS_HASHSIZE_SHA256;
|
||||
return SbHeaderLen + cdLength;
|
||||
}
|
||||
|
||||
/// <summary>生成代码签名 blob(SuperBlob 包裹 CodeDirectory)。
|
||||
/// fileData: 整个文件内容(从 0 到签名 blob 起始位置),codeLimit: 文件中代码部分的字节数(=签名 blob 起始偏移)。</summary>
|
||||
public static byte[] Build(byte[] fileData, int codeLimit, string? identifier = null)
|
||||
{
|
||||
string ident = identifier ?? DefaultIdentifier;
|
||||
byte[] identBytes = Encoding.ASCII.GetBytes(ident + "\0");
|
||||
int identLen = identBytes.Length;
|
||||
int nCodeSlots = (codeLimit + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE;
|
||||
|
||||
int identOffset = CdFixedLen;
|
||||
int hashOffset = CdFixedLen + identLen;
|
||||
int cdLength = hashOffset + nCodeSlots * CS_HASHSIZE_SHA256;
|
||||
|
||||
// ---- CodeDirectory ----
|
||||
var cd = new List<byte>(cdLength);
|
||||
Write32BE(cd, CSMAGIC_CODEDIRECTORY); // magic
|
||||
Write32BE(cd, (uint)cdLength); // length
|
||||
Write32BE(cd, 0x20001); // version
|
||||
Write32BE(cd, 0); // flags (ad-hoc)
|
||||
Write32BE(cd, (uint)hashOffset); // hashOffset
|
||||
Write32BE(cd, (uint)identOffset); // identOffset
|
||||
Write32BE(cd, 0); // nSpecialSlots
|
||||
Write32BE(cd, (uint)nCodeSlots); // nCodeSlots
|
||||
Write32BE(cd, (uint)codeLimit); // codeLimit
|
||||
cd.Add(CS_HASHSIZE_SHA256); // hashSize
|
||||
cd.Add(CS_HASHTYPE_SHA256); // hashType
|
||||
cd.Add(0); // platform
|
||||
cd.Add(CS_PAGE_SHIFT); // pageSize (log2)
|
||||
Write32BE(cd, 0); // spare2
|
||||
cd.AddRange(identBytes); // identifier (null-terminated)
|
||||
// 代码页 SHA-256 哈希
|
||||
using var sha256 = SHA256.Create();
|
||||
for (int i = 0; i < nCodeSlots; i++)
|
||||
{
|
||||
int pageOff = i * CS_PAGE_SIZE;
|
||||
int pageLen = Math.Min(CS_PAGE_SIZE, codeLimit - pageOff);
|
||||
byte[] hash = sha256.ComputeHash(fileData, pageOff, pageLen);
|
||||
cd.AddRange(hash);
|
||||
}
|
||||
|
||||
// ---- SuperBlob ----
|
||||
int sbLength = SbHeaderLen + cdLength;
|
||||
var sb = new List<byte>(sbLength);
|
||||
Write32BE(sb, CSMAGIC_EMBEDDED_SIGNATURE); // magic
|
||||
Write32BE(sb, (uint)sbLength); // length
|
||||
Write32BE(sb, 1); // count (1 blob)
|
||||
Write32BE(sb, CSSLOT_CODEDIRECTORY); // index[0].type
|
||||
Write32BE(sb, (uint)SbHeaderLen); // index[0].offset
|
||||
sb.AddRange(cd); // CodeDirectory
|
||||
|
||||
return sb.ToArray();
|
||||
}
|
||||
|
||||
private static void Write32BE(List<byte> b, uint v)
|
||||
{
|
||||
b.Add((byte)(v >> 24));
|
||||
b.Add((byte)(v >> 16));
|
||||
b.Add((byte)(v >> 8));
|
||||
b.Add((byte)v);
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public sealed class MachOWriter : IExecutableWriter
|
||||
private const uint LC_LOAD_DYLINKER = 0x0E;
|
||||
private const uint LC_DYSYMTAB = 0x0B;
|
||||
private const uint LC_MAIN = 0x80000028;
|
||||
private const uint LC_CODE_SIGNATURE = 0x1D;
|
||||
private const int VM_PROT_READ = 1, VM_PROT_WRITE = 2, VM_PROT_EXECUTE = 4;
|
||||
private const int S_NON_LAZY_SYMBOL_POINTERS = 0x06;
|
||||
// nlist n_type
|
||||
@@ -158,8 +159,9 @@ public sealed class MachOWriter : IExecutableWriter
|
||||
int lcMain = 24;
|
||||
int lcSymtab = 24;
|
||||
int lcDysymtab = 80;
|
||||
int sizeofcmds = segTextCmd + segDataCmd + segLinkCmd + lcDylinker + lcDylib + lcMain + lcSymtab + lcDysymtab;
|
||||
int ncmds = 8;
|
||||
int lcCodeSig = 16; // linkedit_data_command(cmd+cmdsize+dataoff+datasize)
|
||||
int sizeofcmds = segTextCmd + segDataCmd + segLinkCmd + lcDylinker + lcDylib + lcMain + lcSymtab + lcDysymtab + lcCodeSig;
|
||||
int ncmds = 9;
|
||||
|
||||
int headerSize = 32;
|
||||
int textFileOff = headerSize + sizeofcmds; // __text 文件偏移
|
||||
@@ -190,7 +192,11 @@ public sealed class MachOWriter : IExecutableWriter
|
||||
long symtabOff = bindOff + bind.Count;
|
||||
long indirectOff = symtabOff + symtab.Count;
|
||||
long strtabOff = indirectOff + indirect.Count;
|
||||
long linkSegFileEnd = strtabOff + strtab.Count;
|
||||
// 代码签名(ad-hoc):16 字节对齐后附加到 __LINKEDIT 末尾
|
||||
long sigOff = Align(strtabOff + strtab.Count, 16);
|
||||
int codeLimit = (int)sigOff;
|
||||
int sigBlobSize = MachOCodeSignature.ComputeBlobSize(codeLimit);
|
||||
long linkSegFileEnd = sigOff + sigBlobSize;
|
||||
long linkSegVmEnd = linkSegVmaddr + (linkSegFileEnd - linkSegFileOff);
|
||||
|
||||
// ---- 填充 main 符号 n_value ----
|
||||
@@ -325,6 +331,12 @@ public sealed class MachOWriter : IExecutableWriter
|
||||
Write32At(f2, 0); Write32At(f2, 0); // extreloff, nextrel
|
||||
Write32At(f2, 0); Write32At(f2, 0); // locreloff, nlocrel
|
||||
|
||||
// ---- LC_CODE_SIGNATURE ----
|
||||
Write32At(f2, LC_CODE_SIGNATURE);
|
||||
Write32At(f2, 16); // cmdsize (linkedit_data_command)
|
||||
Write32At(f2, (uint)sigOff); // dataoff
|
||||
Write32At(f2, (uint)sigBlobSize); // datasize
|
||||
|
||||
// ---- __TEXT 段数据 ----
|
||||
f2.AddRange(text);
|
||||
f2.AddRange(cstring);
|
||||
@@ -341,6 +353,10 @@ public sealed class MachOWriter : IExecutableWriter
|
||||
f2.AddRange(symtab);
|
||||
f2.AddRange(indirect);
|
||||
f2.AddRange(strtab);
|
||||
// 代码签名(ad-hoc SHA-256):对文件 [0..codeLimit) 逐页哈希
|
||||
while (f2.Count < sigOff) f2.Add(0);
|
||||
byte[] sigBlob = MachOCodeSignature.Build(f2.ToArray(), codeLimit);
|
||||
f2.AddRange(sigBlob);
|
||||
|
||||
return f2.ToArray();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ public sealed class MachOWriterArm64 : IExecutableWriter
|
||||
private const uint LC_LOAD_DYLINKER = 0x0E;
|
||||
private const uint LC_DYSYMTAB = 0x0B;
|
||||
private const uint LC_MAIN = 0x80000028;
|
||||
private const uint LC_CODE_SIGNATURE = 0x1D;
|
||||
private const int VM_PROT_READ = 1, VM_PROT_WRITE = 2, VM_PROT_EXECUTE = 4;
|
||||
private const int S_NON_LAZY_SYMBOL_POINTERS = 0x06;
|
||||
private const byte N_EXT = 0x01, N_SECT = 0x0e;
|
||||
@@ -137,8 +138,9 @@ public sealed class MachOWriterArm64 : IExecutableWriter
|
||||
int lcMain = 24;
|
||||
int lcSymtab = 24;
|
||||
int lcDysymtab = 80;
|
||||
int sizeofcmds = segTextCmd + segDataCmd + segLinkCmd + lcDylinker + lcDylib + lcMain + lcSymtab + lcDysymtab;
|
||||
int ncmds = 8;
|
||||
int lcCodeSig = 16; // linkedit_data_command(cmd+cmdsize+dataoff+datasize)
|
||||
int sizeofcmds = segTextCmd + segDataCmd + segLinkCmd + lcDylinker + lcDylib + lcMain + lcSymtab + lcDysymtab + lcCodeSig;
|
||||
int ncmds = 9;
|
||||
|
||||
int headerSize = 32;
|
||||
int textFileOff = headerSize + sizeofcmds;
|
||||
@@ -168,7 +170,11 @@ public sealed class MachOWriterArm64 : IExecutableWriter
|
||||
long symtabOff = bindOff + bind.Count;
|
||||
long indirectOff = symtabOff + symtab.Count;
|
||||
long strtabOff = indirectOff + indirect.Count;
|
||||
long linkSegFileEnd = strtabOff + strtab.Count;
|
||||
// 代码签名(ad-hoc):16 字节对齐后附加到 __LINKEDIT 末尾
|
||||
long sigOff = Align(strtabOff + strtab.Count, 16);
|
||||
int codeLimit = (int)sigOff;
|
||||
int sigBlobSize = MachOCodeSignature.ComputeBlobSize(codeLimit);
|
||||
long linkSegFileEnd = sigOff + sigBlobSize;
|
||||
long linkSegVmEnd = linkSegVmaddr + (linkSegFileEnd - linkSegFileOff);
|
||||
|
||||
// ---- 填充 main 符号 n_value ----
|
||||
@@ -323,6 +329,12 @@ public sealed class MachOWriterArm64 : IExecutableWriter
|
||||
Write32At(f2, 0); Write32At(f2, 0);
|
||||
Write32At(f2, 0); Write32At(f2, 0);
|
||||
|
||||
// ---- LC_CODE_SIGNATURE ----
|
||||
Write32At(f2, LC_CODE_SIGNATURE);
|
||||
Write32At(f2, 16); // cmdsize (linkedit_data_command)
|
||||
Write32At(f2, (uint)sigOff); // dataoff
|
||||
Write32At(f2, (uint)sigBlobSize); // datasize
|
||||
|
||||
// ---- __TEXT 段数据 ----
|
||||
f2.AddRange(text);
|
||||
f2.AddRange(cstring);
|
||||
@@ -338,6 +350,10 @@ public sealed class MachOWriterArm64 : IExecutableWriter
|
||||
f2.AddRange(symtab);
|
||||
f2.AddRange(indirect);
|
||||
f2.AddRange(strtab);
|
||||
// 代码签名(ad-hoc SHA-256):对文件 [0..codeLimit) 逐页哈希
|
||||
while (f2.Count < sigOff) f2.Add(0);
|
||||
byte[] sigBlob = MachOCodeSignature.Build(f2.ToArray(), codeLimit);
|
||||
f2.AddRange(sigBlob);
|
||||
|
||||
return f2.ToArray();
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户