From 4bb4a2ce303183ed34aba8ad8785bd02e34141d0 Mon Sep 17 00:00:00 2001 From: radare Date: Wed, 12 Sep 2018 16:03:36 +0200 Subject: [PATCH] Parse CodeSign blobs and compute sha1/sha256 in mach0 binaries (#11491) - rabin2 -OC # wip requires proper refactoring and redesign --- binr/rabin2/rabin2.c | 17 +-- libr/bin/Makefile | 2 +- libr/bin/format/mach0/mach0.c | 209 +++++++++++++++++++++++++++++++--- libr/bin/format/mach0/mach0.h | 6 + libr/bin/meson.build | 1 + libr/core/cmd_print.c | 1 + 6 files changed, 214 insertions(+), 22 deletions(-) diff --git a/binr/rabin2/rabin2.c b/binr/rabin2/rabin2.c index b726be1089..d4327d9509 100644 --- a/binr/rabin2/rabin2.c +++ b/binr/rabin2/rabin2.c @@ -406,12 +406,12 @@ static int rabin_do_operation(const char *op) { break; case 'C': { - RBinFile *cur = r_bin_cur (bin); + RBinFile *cur = r_bin_cur (bin); RBinPlugin *plg = r_bin_file_cur_plugin (cur); if (!plg) { - //are we in xtr? + // are we in xtr? if (cur->xtr_data) { - //load the first one + // load the first one RBinXtrData *xtr_data = r_list_get_n (cur->xtr_data, 0); if (!r_bin_file_object_new_from_xtr_data (bin, cur, UT64_MAX, r_bin_get_laddr (bin), xtr_data)) { @@ -424,10 +424,12 @@ static int rabin_do_operation(const char *op) { } } if (plg->signature) { - const char *sign = plg->signature (cur, rad == R_CORE_BIN_JSON); - r_cons_println (sign); - r_cons_flush (); - free ((char*) sign); + char *sign = plg->signature (cur, rad == R_CORE_BIN_JSON); + if (sign) { + r_cons_println (sign); + r_cons_flush (); + free (sign); + } } } break; @@ -737,6 +739,7 @@ int main(int argc, char **argv) { case 'O': op = optarg; set_action (R_BIN_REQ_OPERATION); + r_sys_setenv ("RABIN2_CODESIGN_VERBOSE", "1"); if (isBinopHelp (op)) { printf ("Usage: iO [expression]:\n" " e/0x8048000 change entrypoint\n" diff --git a/libr/bin/Makefile b/libr/bin/Makefile index 78ade6532d..d007fcff74 100644 --- a/libr/bin/Makefile +++ b/libr/bin/Makefile @@ -2,7 +2,7 @@ include ../config.mk include ../../global.mk NAME=r_bin -DEPS=r_util r_io r_socket r_magic +DEPS=r_util r_io r_socket r_magic r_hash .PHONY: pre diff --git a/libr/bin/format/mach0/mach0.c b/libr/bin/format/mach0/mach0.c index 852733b5bc..6449a10c0e 100644 --- a/libr/bin/format/mach0/mach0.c +++ b/libr/bin/format/mach0/mach0.c @@ -4,6 +4,7 @@ #include #include #include "mach0.h" +#include #define bprintf if (bin->verbose) eprintf @@ -12,7 +13,7 @@ typedef struct _ulebr { } ulebr; // OMG; THIS SHOULD BE KILLED; this var exposes the local native endian, which is completely unnecessary -static bool little_; +static bool little_ = false; static ut64 read_uleb128(ulebr *r, ut8 *end) { ut64 result = 0; @@ -536,6 +537,127 @@ static int parse_dysymtab(struct MACH0_(obj_t)* bin, ut64 off) { return true; } +static char *readString (ut8 *p, int off, int len) { + if (off < 0 || off >= len) { + return NULL; + } + return r_str_ndup ((const char *)p + off, len - off); +} + +static void parseCodeDirectory (RBuffer *b, int offset, int datasize) { + 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 platform; /* 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 */ + uint32_t scatterOffset; + uint32_t teamIDOffset; + uint32_t spare3; + ut64 codeLimit64; + ut64 execSegBase; + ut64 execSegLimit; + ut64 execSegFlags; + } CS_CodeDirectory; + ut64 off = offset; + int psize = datasize; + ut8 *p = calloc (1, psize); + if (!p) { + return; + } + eprintf ("Offset: 0x%08"PFMT64x"\n", off); + r_buf_read_at (b, off, p, datasize); + CS_CodeDirectory cscd = {0}; + #define READFIELD(x) cscd.x = r_read_ble32 (p + r_offsetof (CS_CodeDirectory, x), 1) + #define READFIELD8(x) cscd.x = p[r_offsetof (CS_CodeDirectory, x)] + READFIELD (length); + READFIELD (version); + READFIELD (flags); + READFIELD (hashOffset); + READFIELD (identOffset); + READFIELD (nSpecialSlots); + READFIELD (nCodeSlots); + READFIELD (hashSize); + READFIELD (teamIDOffset); + READFIELD8 (hashType); + READFIELD (pageSize); + READFIELD (codeLimit); + eprintf ("Version: %x\n", cscd.version); + eprintf ("Flags: %x\n", cscd.flags); + eprintf ("Length: %d\n", cscd.length); + eprintf ("PageSize: %d\n", cscd.pageSize); + eprintf ("hashOffset: %d\n", cscd.hashOffset); + eprintf ("codeLimit: %d\n", cscd.codeLimit); + eprintf ("hashSize: %d\n", cscd.hashSize); + eprintf ("hashType: %d\n", cscd.hashType); + char *identity = readString (p, cscd.identOffset, psize); + eprintf ("Identity: %s\n", identity); + char *teamId = readString (p, cscd.teamIDOffset, psize); + eprintf ("TeamID: %s\n", teamId); + eprintf ("CodeSlots: %d\n", cscd.nCodeSlots); + free (identity); + free (teamId); + + int hashSize = 20; // SHA1 is default + int algoType = R_HASH_SHA1; + switch (cscd.hashType) { + case 0: // SHA1 == 20 bytes + case 1: // SHA1 == 20 bytes + hashSize = 20; + algoType = R_HASH_SHA1; + break; + case 2: // SHA256 == 32 bytes + hashSize = 32; + algoType = R_HASH_SHA256; + break; + } + ut8 *hash = p + cscd.hashOffset; + int j = 0; + int k = 0; + RHash *ctx = r_hash_new (true, algoType); + for (j = 0; j < cscd.nCodeSlots; j++) { + int fof = 4096 * j; + int idx = j * hashSize; + eprintf ("0x%08"PFMT64x" ", off + cscd.hashOffset + idx); + for (k = 0; k < hashSize; k++) { + eprintf ("%02x", hash[idx + k]); + } + ut8 fofbuf[4096]; + int fofsz = R_MIN (sizeof (fofbuf), cscd.codeLimit - fof); + r_buf_read_at (b, fof, fofbuf, sizeof (fofbuf)); + r_hash_do_begin (ctx, algoType); + if (algoType == R_HASH_SHA1) { + r_hash_do_sha1 (ctx, fofbuf, fofsz); + } else { + r_hash_do_sha256 (ctx, fofbuf, fofsz); + } + r_hash_do_end (ctx, algoType); + if (memcmp (hash + idx, ctx->digest, hashSize)) { + eprintf (" wx "); + int i; + for (i = 0; i < hashSize;i++) { + eprintf ("%02x", ctx->digest[i]); + } + } else { + eprintf (" OK"); + } + eprintf ("\n"); + } + r_hash_free (ctx); + free (p); +} + +// parse the Load Command static bool parse_signature(struct MACH0_(obj_t) *bin, ut64 off) { int i,len; ut32 data; @@ -566,7 +688,18 @@ static bool parse_signature(struct MACH0_(obj_t) *bin, ut64 off) { super.blob.magic = r_read_ble32 (bin->b->buf + data, little_); super.blob.length = r_read_ble32 (bin->b->buf + data + 4, little_); super.count = r_read_ble32 (bin->b->buf + data + 8, little_); - for (i = 0; i < super.count; ++i) { + char *verbose = r_sys_getenv ("RABIN2_CODESIGN_VERBOSE"); + bool isVerbose = false; + if (verbose) { + isVerbose = *verbose; + free (verbose); + } + // to dump all certificates + // [0x00053f75]> b 5K;/x 30800609;wtf @@ hit* + // then do this: + // $ openssl asn1parse -inform der -in a|less + // $ openssl pkcs7 -inform DER -print_certs -text -in a + for (i = 0; i < super.count; i++) { if ((ut8 *)(bin->b->buf + data + i) > (ut8 *)(bin->b->buf + bin->size)) { bin->signature = (ut8 *)strdup ("Malformed entitlement"); break; @@ -578,7 +711,9 @@ static bool parse_signature(struct MACH0_(obj_t) *bin, ut64 off) { } idx.type = r_read_ble32 (&bi.type, little_); idx.offset = r_read_ble32 (&bi.offset, little_); - if (idx.type == CSSLOT_ENTITLEMENTS) { + switch (idx.type) { + case CSSLOT_ENTITLEMENTS: + if (true || isVerbose) { ut64 off = data + idx.offset; if (off > bin->size || off + sizeof (struct blob_t) > bin->size) { bin->signature = (ut8 *)strdup ("Malformed entitlement"); @@ -590,19 +725,65 @@ static bool parse_signature(struct MACH0_(obj_t) *bin, ut64 off) { len = entitlements.length - sizeof (struct blob_t); if (len <= bin->size && len > 1) { bin->signature = calloc (1, len + 1); - if (bin->signature) { - ut8 *src = bin->b->buf + off + sizeof (struct blob_t); - if (off + sizeof (struct blob_t) + len < bin->b->length) { - memcpy (bin->signature, src, len); - bin->signature[len] = '\0'; - return true; - } - bin->signature = (ut8 *)strdup ("Malformed entitlement"); + if (!bin->signature) { + return false; + } + ut8 *src = bin->b->buf + off + sizeof (struct blob_t); + if (off + sizeof (struct blob_t) + len < bin->b->length) { + memcpy (bin->signature, src, len); + bin->signature[len] = '\0'; return true; } + bin->signature = (ut8 *)strdup ("Malformed entitlement"); + return true; } else { bin->signature = (ut8 *)strdup ("Malformed entitlement"); } + } + break; + case CSSLOT_CODEDIRECTORY: + if (isVerbose) { + parseCodeDirectory (bin->b, data + idx.offset, link.datasize); + } + break; + case 0x10000: + // TODO + break; + case CSSLOT_REQUIREMENTS: +#if 0 + // eprintf ("[TODO] CSSLOT_REQUIREMENTS\n"); + { + ut8 p[5000]; + r_buf_read_at (bin->b, data + idx.offset + 0, p, sizeof (p)); + ut32 count = r_read_ble32 (p, 1); + int i = 0; + ut32 *words = (ut32*)p; +// $ openssl asn1parse -inform der -in x + int fd = open ("DUMP", O_RDWR|O_CREAT, 0644); + if (fd != -1) { + write (fd, words, sizeof(p)); + close (fd); + } +#if 0 + for (i = 0; i < count; i++) { + int n = i * 3; + eprintf ("Type (0x%08x) Offset (0x%08x) Expression (0x%08x)\n", + words[n], words[n+1], words[n+2]); + } +#endif + } +#endif + break; +#if 0 + case CSSLOT_INFOSLOT: // 1; + case CSSLOT_RESOURCEDIR: // 3; + case CSSLOT_APPLICATION: // 4; + // TODO + break; +#endif + default: + eprintf ("Unknown Code signature slot %d\n", idx.type); + break; } } if (!bin->signature) { @@ -2480,9 +2661,9 @@ void MACH0_(mach_headerfields)(RBinFile *file) { break; case LC_CODE_SIGNATURE: { - ut32 *words = (ut32*)r_buf_get_at (buf, addr + 4, NULL); - printf ("0x%08"PFMT64x" dataoff 0x%08x\n", addr + 4, words[0]); - printf ("0x%08"PFMT64x" datasize %d\n", addr + 8, words[1]); + ut32 *words = (ut32*)r_buf_get_at (buf, addr, NULL); + printf ("0x%08"PFMT64x" dataoff 0x%08x\n", addr, words[0]); + printf ("0x%08"PFMT64x" datasize %d\n", addr + 4, words[1]); printf ("# wtf mach0.sign %d @ 0x%x\n", words[1], words[0]); } break; diff --git a/libr/bin/format/mach0/mach0.h b/libr/bin/format/mach0/mach0.h index c80337ec1f..e65ba9b631 100644 --- a/libr/bin/format/mach0/mach0.h +++ b/libr/bin/format/mach0/mach0.h @@ -12,10 +12,16 @@ #define CSMAGIC_CODEDIRECTORY 0xfade0c02 #define CSMAGIC_EMBEDDED_SIGNATURE 0xfade0cc0 +#define CSMAGIC_DETACHED_SIGNATURE 0xfade0cc1 /* multi-arch collection of embedded signatures */ #define CSMAGIC_ENTITLEMENTS 0xfade7171 +#define CSMAGIC_REQUIREMENT 0xfade0c00 /* single Requirement blob */ +#define CSMAGIC_REQUIREMENTS 0xfade0c01 /* Requirements vector (internal requirements) */ #define CSSLOT_CODEDIRECTORY 0 +#define CSSLOT_INFOSLOT 1 #define CSSLOT_REQUIREMENTS 2 +#define CSSLOT_RESOURCEDIR 3 +#define CSSLOT_APPLICATION 4 #define CSSLOT_ENTITLEMENTS 5 struct section_t { diff --git a/libr/bin/meson.build b/libr/bin/meson.build index 0067b0b4f2..5171a30d9e 100644 --- a/libr/bin/meson.build +++ b/libr/bin/meson.build @@ -123,6 +123,7 @@ r_bin = library('r_bin', files, dependencies: [ r_util_dep, r_io_dep, + r_hash_dep, r_magic_dep, r_socket_dep, sdb_dep, diff --git a/libr/core/cmd_print.c b/libr/core/cmd_print.c index 73b51ef6b5..71b37bb94f 100644 --- a/libr/core/cmd_print.c +++ b/libr/core/cmd_print.c @@ -247,6 +247,7 @@ static const char *help_msg_pc[] = { "pcs", "", "string", "pcS", "", "shellscript that reconstructs the bin", "pcw", "", "C words (4 byte)", + NULL }; static const char *help_msg_pd[] = {