From 64f0a0d422fb74482815574745ac66e3270bb549 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 12 Nov 2020 21:05:32 +0100 Subject: [PATCH] Add draft parsing of codesign structs --- src/ZachO.zig | 112 ++++++++++++++++++++++++++++++++++++++++++++--- src/machoext.zig | 82 +++++++++++++++++++++++++++++++++- 2 files changed, 185 insertions(+), 9 deletions(-) diff --git a/src/ZachO.zig b/src/ZachO.zig index e83ee42..2e16134 100644 --- a/src/ZachO.zig +++ b/src/ZachO.zig @@ -280,7 +280,7 @@ fn formatData(self: ZachO, seg: SegmentCommand, writer: anytype) !void { try writer.print(" {},{}\n", .{ sect.segname, sect.sectname }); try writer.print(" file = {{ {}, {} }}\n", .{ file_start, file_end }); try writer.print(" address = {{ 0x{x:0<16}, 0x{x:0<16} }}\n\n", .{ addr_start, addr_end }); - try formatBinaryBlob(self.data.items[file_start..file_end], writer); + try formatBinaryBlob(self.data.items[file_start..file_end], " ", writer); try writer.print("\n", .{}); } } @@ -293,22 +293,120 @@ fn formatCodeSignatureData(self: ZachO, csig: CodeSignatureCommand, writer: anyt try writer.print("Code signature data:\n", .{}); try writer.print("file = {{ {}, {} }}\n\n", .{ start_pos, end_pos }); - try formatBinaryBlob(self.data.items[start_pos..end_pos], writer); - try writer.print("\n", .{}); + + var data = self.data.items[start_pos..end_pos]; + const magic = mem.readIntBig(u32, data[0..4]); + const length = mem.readIntBig(u32, data[4..8]); + const count = mem.readIntBig(u32, data[8..12]); + data = data[12..]; + + try writer.print("{{\n", .{}); + try writer.print(" Magic = 0x{x}\n", .{magic}); + try writer.print(" Length = {}\n", .{length}); + try writer.print(" Count = {}\n", .{count}); + try writer.print("}}\n", .{}); + + if (magic != machoext.CSMAGIC_EMBEDDED_SIGNATURE) { + try writer.print("unknown signature type: 0x{x}\n", .{magic}); + try formatBinaryBlob(self.data.items[start_pos..end_pos], null, writer); + return; + } + + var i: usize = 0; + while (i < count) : (i += 1) { + const tt = mem.readIntBig(u32, data[0..4]); // ignored for some reason? + const offset = mem.readIntBig(u32, data[4..8]); + + try writer.print("{{\n", .{}); + + const tt_fmt = switch (tt) { + machoext.CSSLOT_CODEDIRECTORY => "CSSLOT_CODEDIRECTORY", + machoext.CSSLOT_REQUIREMENTS => "CSSLOT_REQUIREMENTS", + machoext.CSSLOT_ALTERNATE_CODEDIRECTORIES => "CSSLOT_ALTERNATE_CODEDIRECTORIES", + machoext.CSSLOT_SIGNATURESLOT => "CSSLOT_SIGNATURESLOT", + else => "Unknown", + }; + try writer.print(" Type: {}(0x{x})\n", .{ tt_fmt, tt }); + try writer.print(" Offset: {}\n", .{offset}); + + var inner = data[offset - 12 - i * 8 ..]; + const magic2 = mem.readIntBig(u32, inner[0..4]); + const length2 = mem.readIntBig(u32, inner[4..8]); + const magic2_fmt = switch (magic2) { + machoext.CSMAGIC_REQUIREMENTS => "CSMAGIC_REQUIREMENTS", + machoext.CSMAGIC_CODEDIRECTORY => "CSMAGIC_CODEDIRECTORY", + machoext.CSMAGIC_BLOBWRAPPER => "CSMAGIC_BLOBWRAPPER", + else => "Unknown", + }; + + try writer.print(" Magic: {}(0x{x})\n", .{ magic2_fmt, magic2 }); + try writer.print(" Length: {}\n", .{length2}); + + switch (magic2) { + machoext.CSMAGIC_CODEDIRECTORY => { + const version = mem.readIntBig(u32, inner[8..12]); + try writer.print(" Version: 0x{x}\n", .{version}); + try writer.print(" Flags: 0x{x}\n", .{mem.readIntBig(u32, inner[12..16])}); + try writer.print(" Hash offset: {}\n", .{mem.readIntBig(u32, inner[16..20])}); + try writer.print(" Ident offset: {}\n", .{mem.readIntBig(u32, inner[20..24])}); + try writer.print(" Number of special slots: {}\n", .{mem.readIntBig(u32, inner[24..28])}); + try writer.print(" Number of code slots: {}\n", .{mem.readIntBig(u32, inner[32..36])}); + try writer.print(" Code limit: {}\n", .{mem.readIntBig(u32, inner[40..44])}); + try writer.print(" Hash size: {}\n", .{mem.readIntBig(u8, inner[44..45])}); + try writer.print(" Hash type: {}\n", .{mem.readIntBig(u8, inner[45..46])}); + try writer.print(" Platform: {}\n", .{mem.readIntBig(u8, inner[46..47])}); + try writer.print(" Page size: {}\n", .{mem.readIntBig(u8, inner[47..48])}); + try writer.print(" Reserved: {}\n", .{mem.readIntBig(u32, inner[48..52])}); + + const len = blk: { + switch (version) { + 0x20400 => { + try writer.print(" Offset of executable segment: {}\n", .{mem.readIntBig(u64, inner[52..60])}); + try writer.print(" Limit of executable segment: {}\n", .{mem.readIntBig(u64, inner[60..68])}); + try writer.print(" Executable segment flags: 0x{x}\n", .{mem.readIntBig(u64, inner[68..76])}); + inner = inner[76..]; + break :blk length2 - 76; + }, + 0x20100 => { + try writer.print(" Offset of optional scatter vector: {}\n", .{mem.readIntBig(u32, inner[52..56])}); + inner = inner[56..]; + break :blk length2 - 56; + }, + else => { + inner = inner[52..]; + break :blk length2 - 52; + } + } + }; + try writer.print(" Data still to parse:\n", .{}); + try formatBinaryBlob(inner[0..len], " ", writer); + }, + else => { + try writer.print(" Data:\n", .{}); + try formatBinaryBlob(inner[8..length2], " ", writer); + }, + } + + try writer.print("}}\n", .{}); + + data = data[8..]; + } } -fn formatBinaryBlob(blob: []const u8, writer: anytype) !void { +fn formatBinaryBlob(blob: []const u8, prefix: ?[]const u8, writer: anytype) !void { // Format as 16-by-16-by-8 with two left column in hex, and right in ascii: // xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxx var i: usize = 0; const step = 16; - while (i <= blob.len) : (i += step) { + const pp = prefix orelse ""; + while (i < blob.len) : (i += step) { if (blob[i..].len < step / 2) { - try writer.print("{x:<033} {e}\n", .{ blob[i..], blob[i..] }); + try writer.print("{}{x:<033} {}\n", .{ pp, blob[i..], blob[i..] }); continue; } const rem = std.math.min(blob[i..].len, step); - try writer.print("{x:<016} {x:<016} {e}\n", .{ + try writer.print("{}{x:<016} {x:<016} {}\n", .{ + pp, blob[i .. i + rem / 2], blob[i + rem / 2 .. i + rem], blob[i .. i + rem], diff --git a/src/machoext.zig b/src/machoext.zig index 5989346..b7318b4 100644 --- a/src/machoext.zig +++ b/src/machoext.zig @@ -7,16 +7,94 @@ pub const code_signature = extern struct { datasize: u32, }; -pub const CSMAGIC_EMBEDDED_SIGNATURE: u32 = 0xFADE0CC0; +/// single Requirement blob +pub const CSMAGIC_REQUIREMENT: u32 = 0xfade0c00; +/// Requirements vector (internal requirements) +pub const CSMAGIC_REQUIREMENTS: u32 = 0xfade0c01; +/// CodeDirectory blob +pub const CSMAGIC_CODEDIRECTORY: u32 = 0xfade0c02; +/// embedded form of signature data +pub const CSMAGIC_EMBEDDED_SIGNATURE: u32 = 0xfade0cc0; +/// XXX +pub const CSMAGIC_EMBEDDED_SIGNATURE_OLD: u32 = 0xfade0b02; +/// embedded entitlements +pub const CSMAGIC_EMBEDDED_ENTITLEMENTS: u32 = 0xfade7171; +/// multi-arch collection of embedded signatures +pub const CSMAGIC_DETACHED_SIGNATURE: u32 = 0xfade0cc1; +/// CMS Signature, among other things +pub const CSMAGIC_BLOBWRAPPER: u32 = 0xfade0b01; + +pub const CS_SUPPORTSSCATTER: u32 = 0x20100; +pub const CS_SUPPORTSTEAMID: u32 = 0x20200; +pub const CS_SUPPORTSCODELIMIT64: u32 = 0x20300; +pub const CS_SUPPORTSEXECSEG: u32 = 0x20400; + +/// slot index for CodeDirectory +pub const CSSLOT_CODEDIRECTORY: u32 = 0; +pub const CSSLOT_INFOSLOT: u32 = 1; +pub const CSSLOT_REQUIREMENTS: u32 = 2; +pub const CSSLOT_RESOURCEDIR: u32 = 3; +pub const CSSLOT_APPLICATION: u32 = 4; +pub const CSSLOT_ENTITLEMENTS: u32 = 5; + +/// first alternate CodeDirectory, if any +pub const CSSLOT_ALTERNATE_CODEDIRECTORIES: u32 = 0x1000; +/// max number of alternate CD slots */ +pub const CSSLOT_ALTERNATE_CODEDIRECTORY_MAX: u32 = 5; +/// one past the last +pub const CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT: u32 = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX; + +/// CMS Signature +pub const CSSLOT_SIGNATURESLOT: u32 = 0x10000; +pub const CSSLOT_IDENTIFICATIONSLOT: u32 = 0x10001; +pub const CSSLOT_TICKETSLOT: u32 = 0x10002; + +/// compat with amfi +pub const CSTYPE_INDEX_REQUIREMENTS: u32 = 0x00000002; +/// compat with amfi +pub const CSTYPE_INDEX_ENTITLEMENTS: u32 = 0x00000005; + +pub const CS_HASHTYPE_SHA1: u32 = 1; +pub const CS_HASHTYPE_SHA256: u32 = 2; +pub const CS_HASHTYPE_SHA256_TRUNCATED: u32 = 3; +pub const CS_HASHTYPE_SHA384: u32 = 4; + +pub const CS_SHA1_LEN: u32 = 20; +pub const CS_SHA256_LEN: u32 = 32; +pub const CS_SHA256_TRUNCATED_LEN: u32 = 20; + +/// always - larger hashes are truncated +pub const CS_CDHASH_LEN: u32 = 20; +/// max size of the hash we'll support +pub const CS_HASH_MAX_SIZE: u32 = 48; pub const SuperBlob = extern struct { magic: u32, length: u32, count: u32, - index: ?*BlobIndex, }; pub const BlobIndex = extern struct { @"type": u32, offset: u32, }; + +pub const CodeDirectory = extern struct { + magic: u32, + length: u32, + version: u32, + flags: u32, + hashOffset: u32, + identOffset: u32, + nSpecialSlots: u32, + nCodeSlots: u32, + codeLimit: u32, + hashSize: u8, + hashType: u8, + platform: u8, + pageSize: u8, + spare2: u32, + execSegBase: u64, + execSegLimit: u64, + execSegFlags: u64, +};