Parse CodeSign blobs and compute sha1/sha256 in mach0 binaries (#11491)

- rabin2 -OC # wip requires proper refactoring and redesign
This commit is contained in:
radare 2018-09-12 16:03:36 +02:00 committed by GitHub
parent 08f8e48ca1
commit 4bb4a2ce30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 214 additions and 22 deletions

View File

@ -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"

View File

@ -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

View File

@ -4,6 +4,7 @@
#include <r_types.h>
#include <r_util.h>
#include "mach0.h"
#include <r_hash.h>
#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;

View File

@ -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 {

View File

@ -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,

View File

@ -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[] = {