From 3f7eef64da6a0090e872a19630e572b2d2c0e550 Mon Sep 17 00:00:00 2001 From: JGZ_YES Date: Mon, 3 Aug 2026 21:59:05 +0800 Subject: [PATCH] fix(macos): add ad-hoc code signature to generated Mach-O MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../Binary/MachOCodeSignature.cs | 94 +++++++++++++++++++ src/PazeE.Compiler/Binary/MachOWriter.cs | 22 ++++- src/PazeE.Compiler/Binary/MachOWriterArm64.cs | 22 ++++- 3 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 src/PazeE.Compiler/Binary/MachOCodeSignature.cs diff --git a/src/PazeE.Compiler/Binary/MachOCodeSignature.cs b/src/PazeE.Compiler/Binary/MachOCodeSignature.cs new file mode 100644 index 0000000..dd1794c --- /dev/null +++ b/src/PazeE.Compiler/Binary/MachOCodeSignature.cs @@ -0,0 +1,94 @@ +using System.Security.Cryptography; +using System.Text; + +namespace PazeE.Compiler.Binary; + +/// 生成 macOS ad-hoc 代码签名(SuperBlob + CodeDirectory,SHA-256)。 +/// macOS 11+(尤其 Apple Silicon)要求所有可执行文件至少有 ad-hoc 签名,否则内核 SIGKILL。 +/// 代码签名 blob 使用大端字节序(CSBlob 规范),与 Mach-O 主体的小端序不同。 +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; + + /// 计算代码签名 blob 的确切大小(字节),用于在写入头之前确定 __LINKEDIT 段尺寸。 + 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; + } + + /// 生成代码签名 blob(SuperBlob 包裹 CodeDirectory)。 + /// fileData: 整个文件内容(从 0 到签名 blob 起始位置),codeLimit: 文件中代码部分的字节数(=签名 blob 起始偏移)。 + 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(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(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 b, uint v) + { + b.Add((byte)(v >> 24)); + b.Add((byte)(v >> 16)); + b.Add((byte)(v >> 8)); + b.Add((byte)v); + } +} diff --git a/src/PazeE.Compiler/Binary/MachOWriter.cs b/src/PazeE.Compiler/Binary/MachOWriter.cs index 3d7e5d9..e2066c5 100644 --- a/src/PazeE.Compiler/Binary/MachOWriter.cs +++ b/src/PazeE.Compiler/Binary/MachOWriter.cs @@ -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(); } diff --git a/src/PazeE.Compiler/Binary/MachOWriterArm64.cs b/src/PazeE.Compiler/Binary/MachOWriterArm64.cs index 130fe97..9e46591 100644 --- a/src/PazeE.Compiler/Binary/MachOWriterArm64.cs +++ b/src/PazeE.Compiler/Binary/MachOWriterArm64.cs @@ -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(); }