ruby-macho icon indicating copy to clipboard operation
ruby-macho copied to clipboard

Pure-Ruby Code Directory/Code signing parsing and manipulation

Open woodruffw opened this issue 5 years ago • 7 comments

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_SIGNATURE references the signing data, but doesn't actually contain it. It's actually hiding in the __LINKEDIT segment. That means that we'll need to rewrite (and probably resize) __LINKEDIT.

woodruffw avatar Sep 29 '20 00:09 woodruffw

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!)

woodruffw avatar Sep 29 '20 00:09 woodruffw

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.

woodruffw avatar Sep 29 '20 00:09 woodruffw

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;

woodruffw avatar Sep 29 '20 00:09 woodruffw

Ongoing in #265.

woodruffw avatar Oct 06 '20 00:10 woodruffw

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

BrewTestBot avatar Dec 15 '20 21:12 BrewTestBot

raises hand - I'm planning to do some work in this space - initially entitlements to replace the aging j's database

rickmark avatar Jan 14 '22 00:01 rickmark

Please do! I've done some initial work in #265; feel free to crib from that if it helps.

woodruffw avatar Jan 14 '22 01:01 woodruffw