Pure-Ruby Code Directory/Code signing parsing and manipulation
I'm going to use this issue as a dumping ground as I explore a pure-Ruby alternative to #260.
At a high level:
- If a binary already contains an
LC_CODE_SIGNATURE, we need to erase it and replace it with our own (ad-hoc) signature - If a binary doesn't contain an
LC_CODE_SIGNATURE, we need to add a new load command containing one
That's not the end of things:
-
LC_CODE_SIGNATUREreferences the signing data, but doesn't actually contain it. It's actually hiding in the__LINKEDITsegment. That means that we'll need to rewrite (and probably resize)__LINKEDIT.
Kicking things off: here's the difference between the headers of an unsigned libgettextlib and an ad-hoc signed one:

12 -> 13 corresponds to the increase in load commands, E0 07 00 00 -> F0 07 00 00 corresponds to the increase in sizeofcmds (16, exactly one linkedit_data_command).
(Thanks @mistydemeo for the samples!)
Here's how codesign.c futzes with that lc_code_signature (within __LINKEDIT):
CS_SuperBlob *sb = (CS_SuperBlob*)lc_code_signature;
and the structs:
typedef struct __SuperBlob {
uint32_t magic; /* magic number */
uint32_t length; /* total length of SuperBlob */
uint32_t count; /* number of index entries following */
CS_BlobIndex index[]; /* (count) entries */
/* followed by Blobs in no particular order as indicated by offsets in index */
} CS_SuperBlob;
typedef struct __BlobIndex {
uint32_t type; /* type of entry */
uint32_t offset; /* offset of entry */
} CS_BlobIndex;
where type is presumably this enum:
enum {
CSMAGIC_REQUIREMENT = 0xfade0c00, /* single Requirement blob */
CSMAGIC_REQUIREMENTS = 0xfade0c01, /* Requirements vector (internal requirements) */
CSMAGIC_CODEDIRECTORY = 0xfade0c02, /* CodeDirectory blob */
CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */
CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */
CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */
};
Apparently this enum isn't complete: comex found two more in their attempt:
CSMAGIC_ENTITLEMENT = 0xfade7171, # actually, this is kSecCodeMagicEntitlement, and not defined in the C version :psyduck:
CSMAGIC_BLOBWRAPPER = 0xfade0b01, # and this isn't even defined in libsecurity_codesigning; it's in _utilities
So it's pretty standard TLV parsing, nothing outside of what ruby-macho can already do. Presumably each type corresponds to a structure of fixed (or inferrable) size.
Oh, maybe type isn't that enum. Might be something else. Anyways, here's the struct for CSMAGIC_CODEDIRECTORY:
typedef struct __CodeDirectory {
uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */
uint32_t length; /* total length of CodeDirectory blob */
uint32_t version; /* compatibility version */
uint32_t flags; /* setup and mode flags */
uint32_t hashOffset; /* offset of hash slot element at index zero */
uint32_t identOffset; /* offset of identifier string */
uint32_t nSpecialSlots; /* number of special hash slots */
uint32_t nCodeSlots; /* number of ordinary (code) hash slots */
uint32_t codeLimit; /* limit to main image signature range */
uint8_t hashSize; /* size of each hash in bytes */
uint8_t hashType; /* type of hash (cdHashType* constants) */
uint8_t spare1; /* unused (must be zero) */
uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */
uint32_t spare2; /* unused (must be zero) */
/* followed by dynamic content as located by offset fields above */
} CS_CodeDirectory;
Ongoing in #265.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
raises hand - I'm planning to do some work in this space - initially entitlements to replace the aging j's database
Please do! I've done some initial work in #265; feel free to crib from that if it helps.