metasploit-framework/external/source/exploits/CVE-2017-13861/liboffsetfinder64/img4.c

1294 lines
42 KiB
C

//
// img4.c
// img4tool
//
// Created by tihmstar on 15.06.16.
// Copyright © 2016 tihmstar. All rights reserved.
//
#include "img4.h"
#include "all_img4tool.h"
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <stdint.h>
#include "lzssdec.h"
/*#ifndef IMG4TOOL_NOLZFSE*/
/*#ifdef HAVE_LIBLZFSE*/
/*# include <lzfse.h>*/
/*#elif defined(HAVE_LIBCOMPRESSION)*/
/*# include <compression.h>*/
/*# define lzfse_decode_buffer(src, src_size, dst, dst_size, scratch) \*/
/*compression_decode_buffer(src, src_size, dst, dst_size, scratch, COMPRESSION_LZFSE)*/
/*#else*/
/*# error "either liblzfse or libcompression is needed"*/
/*#endif*/
/*#endif*/
/*#ifndef IMG4TOOL_NOOPENSSL*/
/*# include <openssl/x509.h>*/
/*# include <openssl/evp.h>*/
/*# include <openssl/sha.h>*/
/*#elif defined(__APPLE__)*/
/*# include <CommonCrypto/CommonDigest.h>*/
/*# define SHA1(d, n, md) CC_SHA1(d, n, md)*/
/*# define SHA_DIGEST_LENGTH CC_SHA1_DIGEST_LENGTH*/
/*#else*/
/*# error openssl is required on non-Apple*/
/*#endif*/
#define safeFree(buf) if (buf) free(buf), buf = NULL
#define assure(a) do{ if ((a) == 0){err=1; goto error;} }while(0)
#define retassure(retcode, a) do{ if ((a) == 0){err=retcode; goto error;} }while(0)
#define asn1Tag(a) ((t_asn1Tag*)a)
t_asn1ElemLen asn1Len(const char buf[4]){
t_asn1Length *sTmp = (t_asn1Length *)buf;
size_t outSize = 0;
int sizeBytes_ = 0;
unsigned char *sbuf = (unsigned char *)buf;
if (!sTmp->isLong) outSize = sTmp->len;
else{
sizeBytes_ = sTmp->len;
for (int i=0; i<sizeBytes_; i++) {
outSize *= 0x100;
outSize += sbuf[1+i];
}
}
t_asn1ElemLen ret;
ret.dataLen = outSize;
ret.sizeBytes = sizeBytes_+1;
return ret;
}
char *ans1GetString(char *buf, char **outString, size_t *strlen){
t_asn1Tag *tag = (t_asn1Tag *)buf;
if (!(tag->tagNumber | kASN1TagIA5String)) {
error("not a string\n");
return 0;
}
t_asn1ElemLen len = asn1Len(++buf);
*strlen = len.dataLen;
buf+=len.sizeBytes;
if (outString) *outString = buf;
return buf+*strlen;
}
int asn1ElementAtIndexWithCounter(const char *buf, int index, t_asn1Tag **tagret){
int ret = 0;
if (!((t_asn1Tag *)buf)->isConstructed) return 0;
t_asn1ElemLen len = asn1Len(++buf);
buf +=len.sizeBytes;
while (len.dataLen) {
if (ret == index && tagret){
*tagret = (t_asn1Tag*)buf;
return ret;
}
if (*buf == kASN1TagPrivate) {
size_t sb;
asn1GetPrivateTagnum((t_asn1Tag*)buf,&sb);
buf+=sb;
len.dataLen-=sb;
}else if (*buf == (char)0x9F){
//buf is element in set and it's value is encoded in the next byte
t_asn1ElemLen l = asn1Len(++buf);
if (l.sizeBytes > 1) l.dataLen += 0x80;
buf += l.sizeBytes;
len.dataLen -= 1 + l.sizeBytes;
}else
buf++,len.dataLen--;
t_asn1ElemLen sublen = asn1Len(buf);
size_t toadd =sublen.dataLen + sublen.sizeBytes;
len.dataLen -=toadd;
buf +=toadd;
ret ++;
}
return ret;
}
int asn1ElementsInObject(const char *buf){
return asn1ElementAtIndexWithCounter(buf, -1, NULL);
}
char *asn1ElementAtIndex(const char *buf, int index){
t_asn1Tag *ret;
asn1ElementAtIndexWithCounter(buf, index, &ret);
return (char*)ret;
}
int getSequenceName(const char *buf,char**name, size_t *nameLen){
#define reterror(a ...){error(a); err = -1; goto error;}
int err = 0;
if (((t_asn1Tag*)buf)->tagNumber != kASN1TagSEQUENCE) reterror("not a SEQUENCE\n");
int elems = asn1ElementsInObject(buf);
if (!elems) reterror("no elements in SEQUENCE\n");
size_t len;
ans1GetString((char*)asn1ElementAtIndex(buf,0),name,&len);
if (nameLen) *nameLen = len;
error:
return err;
#undef reterror
}
size_t asn1GetPrivateTagnum(t_asn1Tag *tag, size_t *sizebytes){
if (*(unsigned char*)tag != 0xff) {
error("not a private TAG 0x%02x\n",*(unsigned int*)tag);
return 0;
}
size_t sb = 1;
t_asn1ElemLen taglen = asn1Len((char*)++tag);
taglen.sizeBytes-=1;
if (taglen.sizeBytes != 4){
/*
WARNING: seems like apple's private tag is always 4 bytes long
i first assumed 0x84 can be parsed as long size with 4 bytes,
but 0x86 also seems to be 4 bytes size even if one would assume it means 6 bytes size.
This opens the question what the 4 or 6 nibble means.
*/
taglen.sizeBytes = 4;
}
size_t tagname =0;
do {
tagname *=0x100;
tagname>>=1;
tagname += ((t_asn1PrivateTag*)tag)->num;
sb++;
} while (((t_asn1PrivateTag*)tag++)->more);
if (sizebytes) *sizebytes = sb;
return tagname;
}
uint64_t ans1GetNumberFromTag(t_asn1Tag *tag){
if (tag->tagNumber != kASN1TagINTEGER) return (error("not an INTEGER\n"),0);
uint64_t ret = 0;
t_asn1ElemLen len = asn1Len((char*)++tag);
unsigned char *data = (unsigned char*)tag+len.sizeBytes;
while (len.dataLen--) {
ret *= 0x100;
ret+= *data++;
}
return ret;
}
void printStringWithKey(char*key, t_asn1Tag *string){
char *str = 0;
size_t strlen;
ans1GetString((char*)string,&str,&strlen);
printf("%s",key);
putStr(str, strlen);
putchar('\n');
}
void printPrivtag(size_t privTag){
char *ptag = (char*)&privTag;
int len = 0;
while (*ptag) ptag++,len++;
while (len--) putchar(*--ptag);
}
void printHexString(t_asn1Tag *str){
if (str->tagNumber != kASN1TagOCTET){
error("not an OCTET string\n");
return;
}
t_asn1ElemLen len = asn1Len((char*)str+1);
unsigned char *string = (unsigned char*)str + len.sizeBytes +1;
while (len.dataLen--) printf("%02x",*string++);
}
void printI5AString(t_asn1Tag *str){
if (str->tagNumber != kASN1TagIA5String){
error("not an I5A string\n");
return;
}
t_asn1ElemLen len = asn1Len((char*)++str);
putStr(((char*)str)+len.sizeBytes, len.dataLen);
}
void printKBAGOctet(char *octet){
#define reterror(a ...){error(a);goto error;}
if (((t_asn1Tag*)octet)->tagNumber != kASN1TagOCTET) reterror("not an OCTET\n");
t_asn1ElemLen octetlen = asn1Len(++octet);
octet +=octetlen.sizeBytes;
//main seq
int subseqs = asn1ElementsInObject(octet);
for (int i=0; i<subseqs; i++) {
char *s = (char*)asn1ElementAtIndex(octet, i);
int elems = asn1ElementsInObject(s);
if (elems--){
//integer (currently unknown?)
t_asn1Tag *num = (t_asn1Tag*)asn1ElementAtIndex(s, 0);
if (num->tagNumber != kASN1TagINTEGER) warning("skipping unexpected tag\n");
else{
char n = *(char*)(num+2);
printf("num: %d\n",n);
}
}
if (elems--)printHexString((t_asn1Tag*)asn1ElementAtIndex(s, 1)),putchar('\n');
if (elems--)printHexString((t_asn1Tag*)asn1ElementAtIndex(s, 2)),putchar('\n');
}
error:
return;
#undef reterror
}
void printNumber(t_asn1Tag *tag){
if (tag->tagNumber != kASN1TagINTEGER) {
error("tag not an INTEGER\n");
return;
}
t_asn1ElemLen len = asn1Len((char*)++tag);
uint32_t num = 0;
while (len.sizeBytes--) {
num *=0x100;
num += *(unsigned char*)++tag;
}
printf("%u",num);
}
void printIM4P(char *buf){
#define reterror(a ...){error(a);goto error;}
char *magic;
size_t l;
getSequenceName(buf, &magic, &l);
if (strncmp("IM4P", magic, l)) reterror("unexpected \"%.*s\", expected \"IM4P\"\n",(int)l,magic);
int elems = asn1ElementsInObject(buf);
if (--elems>0) printStringWithKey("type: ",(t_asn1Tag*)asn1ElementAtIndex(buf, 1));
if (--elems>0) printStringWithKey("desc: ",(t_asn1Tag*)asn1ElementAtIndex(buf, 2));
if (--elems>0) {
//data
t_asn1Tag *data = (t_asn1Tag*)asn1ElementAtIndex(buf, 3);
if (data->tagNumber != kASN1TagOCTET) warning("skipped an unexpected tag where OCTETSTING was expected\n");
else printf("size: 0x%08zx\n",asn1Len((char*)data+1).dataLen);
}
if (--elems>0) {
//kbag values
printf("\nKBAG\n");
printKBAGOctet((char*)asn1ElementAtIndex(buf, 4));
}else{
printf("\nIM4P does not contain KBAG values\n");
}
error:
return;
#undef reterror
}
char* extractPayloadFromIM4P(const char* buf, const char** compname, size_t *len) {
int elems = asn1ElementsInObject(buf);
if (elems < 4) {
error("not enough elements in SEQUENCE: %d", elems);
return NULL;
}
char *name = NULL;
size_t namelen = 0;
char *krnl_tag = asn1ElementAtIndex(buf, 1);
char *rv = ans1GetString(krnl_tag, &name, &namelen);
if (rv == NULL || namelen != 4 || strncmp(name, "krnl", 4) != 0) {
printf("Not a krnl\n");
return NULL;
}
char *dataTag = asn1ElementAtIndex(buf, 3)+1;
t_asn1ElemLen dlen = asn1Len(dataTag);
char *data = dataTag+dlen.sizeBytes;
char *kernel = NULL;
const char* comp = NULL;
if (strncmp(data, "complzss", 8) == 0) {
comp = "lzss";
kernel = tryLZSS(data, len);
} else if (strncmp(data, "bvx2", 4) == 0) {
comp = "lzfse";
#ifndef IMG4TOOL_NOLZFSE
char *compTag = data + dlen.dataLen;
char *fakeCompSizeTag = asn1ElementAtIndex(compTag, 0);
char *uncompSizeTag = asn1ElementAtIndex(compTag, 1);
size_t fake_src_size = ans1GetNumberFromTag(asn1Tag(fakeCompSizeTag));
size_t dst_size = ans1GetNumberFromTag(asn1Tag(uncompSizeTag));
size_t src_size = dlen.dataLen;
if (fake_src_size != 1) {
printf("fake_src_size not 1 but 0x%zx!\n", fake_src_size);
}
kernel = malloc(dst_size);
size_t uncomp_size = lzfse_decode_buffer(
(uint8_t*) kernel, dst_size,
(uint8_t*) data, src_size,
NULL);
if (uncomp_size != dst_size) {
printf("expected to decompress %zu bytes but only got %zu\n", dst_size, uncomp_size);
free(kernel);
kernel = NULL;
} else {
*len = dst_size;
}
#else
printf("Can't unpack data because img4tool was compiled without lzfse!\n");
#endif
}
*compname = comp;
return kernel;
}
/*
int extractFileFromIM4P(char *buf, const char *dstFilename){
int elems = asn1ElementsInObject(buf);
if (elems < 4){
error("not enough elements in SEQUENCE %d\n",elems);
return -2;
}
char *dataTag = asn1ElementAtIndex(buf, 3)+1;
t_asn1ElemLen dlen = asn1Len(dataTag);
char *data = dataTag+dlen.sizeBytes;
char* kernel = NULL;
{
size_t kernel_len = 0;
const char* compname = NULL;
kernel = extractPayloadFromIM4P(buf, &compname, &kernel_len);
if (compname != NULL) {
printf("Kernelcache detected, uncompressing (%s): %s\n", compname, kernel ? "ok" : "failure");
}
if (kernel != NULL) {
data = kernel;
dlen.dataLen = kernel_len;
}
}
FILE *f = fopen(dstFilename, "wb");
if (!f) {
error("can't open file %s\n",dstFilename);
return -1;
}
fwrite(data, dlen.dataLen, 1, f);
fclose(f);
if (kernel)
free(kernel);
return 0;
}
*/
int sequenceHasName(const char *buf, char *name){
char *magic;
size_t l;
int err = getSequenceName(buf, &magic, &l);
return !err && strncmp(name, magic, l) == 0;
}
char *getElementFromIMG4(char *buf, char* element){
#define reterror(a ...) return (error(a),NULL)
if (!sequenceHasName(buf, "IMG4")) reterror("not img4 sequcence\n");
int elems = asn1ElementsInObject(buf);
for (int i=0; i<elems; i++) {
char *elemen = asn1ElementAtIndex(buf, i);
if (asn1Tag(elemen)->tagNumber != kASN1TagSEQUENCE && asn1Tag(elemen)->tagClass == kASN1TagClassContextSpecific) {
//assuming we found a "subcontainer"
elemen += asn1Len((char*)elemen+1).sizeBytes+1;
}
if (asn1Tag(elemen)->tagNumber == kASN1TagSEQUENCE && sequenceHasName(elemen, element)) {
return (char*)elemen;
}
}
reterror("element %s not found in IMG4\n",element);
#undef reterror
}
int extractElementFromIMG4(char *buf, char* element, const char *dstFilename){
#define reterror(a ...) return (error(a),-1)
char *elemen = getElementFromIMG4(buf, element);
if (!elemen) return -1;
FILE *f = fopen(dstFilename, "wb");
if (!f) {
error("can't open file %s\n",dstFilename);
return -1;
}
t_asn1ElemLen len = asn1Len((char*)elemen+1);
size_t flen = len.dataLen + len.sizeBytes +1;
fwrite(elemen, flen, 1, f);
fclose(f);
return 0;
#undef reterror
}
int asn1MakeSize(char *sizeBytesDst, size_t size){
int off = 0;
if (size >= 0x1000000) {
// 1+4 bytes length
sizeBytesDst[off++] = 0x84;
sizeBytesDst[off++] = (size >> 24) & 0xFF;
sizeBytesDst[off++] = (size >> 16) & 0xFF;
sizeBytesDst[off++] = (size >> 8) & 0xFF;
sizeBytesDst[off++] = size & 0xFF;
} else if (size >= 0x10000) {
// 1+3 bytes length
sizeBytesDst[off++] = 0x83;
sizeBytesDst[off++] = (size >> 16) & 0xFF;
sizeBytesDst[off++] = (size >> 8) & 0xFF;
sizeBytesDst[off++] = size & 0xFF;
} else if (size >= 0x100) {
// 1+2 bytes length
sizeBytesDst[off++] = 0x82;
sizeBytesDst[off++] = (size >> 8) & 0xFF;
sizeBytesDst[off++] = (size & 0xFF);
} else if (size >= 0x80) {
// 1+1 byte length
sizeBytesDst[off++] = 0x81;
sizeBytesDst[off++] = (size & 0xFF);
} else {
// 1 byte length
sizeBytesDst[off++] = size & 0xFF;
}
return off;
}
char *asn1PrepandTag(char *buf, t_asn1Tag tag){
t_asn1ElemLen len = asn1Len(buf+1);
//alloc mem for oldTag+oldSizebytes+oldData + newTag + newTagSizebytesMax
char *ret = malloc(len.sizeBytes + len.dataLen +1 +1+4);
ret[0] = *(char*)&tag;
int nSizeBytes = asn1MakeSize(ret+1, len.sizeBytes + len.dataLen +1);
memcpy(ret + nSizeBytes+1, buf, len.sizeBytes + len.dataLen +1);
return ret;
}
char *asn1AppendToTag(char *buf, char *toappend){
t_asn1ElemLen buflen = asn1Len(buf+1);
t_asn1ElemLen apndLen = asn1Len(toappend+1);
//alloc memory for bufdata + buftag + apndData + apndSizebytes + apndTag + maxSizeBytesForBuf
size_t containerLen;
char *ret = malloc(1 +(containerLen = buflen.dataLen +apndLen.sizeBytes + apndLen.dataLen +1) +4);
ret[0] = buf[0];
int nSizeBytes = asn1MakeSize(ret+1, containerLen);
//copy old data
memcpy(ret + nSizeBytes+1, buf+1+buflen.sizeBytes, buflen.dataLen);
memcpy(ret +nSizeBytes+1+ buflen.dataLen, toappend, apndLen.sizeBytes +apndLen.dataLen +1);
free(buf);
return ret;
}
char *makeIM4RWithNonce(char *nonce){
char template[] = {0xA1, 0x23, 0x30, 0x21, 0x16, 0x04, 0x49, 0x4D,
0x34, 0x52, 0x31, 0x19, 0xFF, 0x84, 0x92, 0xB9,
0x86, 0x4E, 0x12, 0x30, 0x10, 0x16, 0x04, 0x42,
0x4E, 0x43, 0x4E, 0x04, 0x08};
char *ret = malloc(sizeof(template)+8);
strncpy(ret, template,sizeof(template));
strncpy(ret+sizeof(template), nonce, 8);
return ret;
}
/*
char *makeIMG4(char *im4p, char *im4m, char *im4r, size_t *size){
t_asn1Tag elem0;
elem0.tagNumber = 0;
elem0.tagClass = kASN1TagClassContextSpecific;
elem0.isConstructed = 1;
if (im4m) im4m = asn1PrepandTag(im4m, elem0);
char *sequence = malloc(2);
sequence[0] = 0x30;
sequence[1] = 0x00;
char iA5String_IMG4[] = {0x16, 0x04, 0x49, 0x4D, 0x47, 0x34};
sequence = asn1AppendToTag(sequence, iA5String_IMG4);
if (im4p) sequence = asn1AppendToTag(sequence, im4p);
if (im4m) sequence = asn1AppendToTag(sequence, im4m);
if (im4r) {
char *noncebuf = makeIM4RWithNonce(im4r);
sequence = asn1AppendToTag(sequence, noncebuf);
free(noncebuf);
}
if (size){
t_asn1ElemLen retlen = asn1Len(sequence+1);
*size = 1+ retlen.dataLen + retlen.sizeBytes;
}
free(im4m); //only freeing local copy, not actually freeing outside im4m buffer
return sequence;
}
int replaceNameInIM4P(char *buf, const char *newName){
if (asn1ElementsInObject(buf)<2){
error("not enough objects in sequence\n");
return -1;
}
char *nameTag = asn1ElementAtIndex(buf, 1);
if (asn1Tag(nameTag)->tagNumber != kASN1TagIA5String){
error("nameTag is not IA5String\n");
return -2;
}
t_asn1ElemLen len;
if ((len = asn1Len(nameTag+1)).dataLen !=4){
error("nameTag has not a length of 4 Bytes, actual len=%ld\n",len.dataLen);
return -2;
}
memmove(nameTag + 1 + len.sizeBytes, newName, 4);
return 0;
}
char *getValueForTagInSet(char *set, uint32_t tag){
#define reterror(a) return (error(a),NULL)
if (((t_asn1Tag*)set)->tagNumber != kASN1TagSET) reterror("not a SET\n");
t_asn1ElemLen setlen = asn1Len(++set);
for (char *setelems = set+setlen.sizeBytes; setelems<set+setlen.dataLen;) {
if (*(unsigned char*)setelems == 0xff) {
//priv tag
size_t sb;
size_t ptag = asn1GetPrivateTagnum((t_asn1Tag*)setelems,&sb);
setelems += sb;
t_asn1ElemLen len = asn1Len(setelems);
setelems += len.sizeBytes;
if (tag == ptag) return setelems;
setelems +=len.dataLen;
}else{
//normal tag
t_asn1ElemLen len = asn1Len(setelems);
setelems += len.sizeBytes + 1;
if (((t_asn1Tag*)setelems)->tagNumber == tag) return setelems;
setelems += len.dataLen;
}
}
return 0;
#undef reterror
}
void printElemsInIMG4(char *buf, bool printAll, bool im4pOnly){
#define reterror(a...) {error(a); goto error;}
char *magic;
size_t l;
getSequenceName(buf, &magic, &l);
if (strncmp("IMG4", magic, l)) reterror("unexpected \"%.*s\", expected \"IMG4\"\n",(int)l,magic);
printf("IMG4:\n");
int elems = asn1ElementsInObject(buf);
for (int i=1; i<elems; i++) {
char *tag = (char*)asn1ElementAtIndex(buf, i);
if (((t_asn1Tag*)tag)->tagClass == kASN1TagClassContextSpecific) {
tag += asn1Len((char*)tag+1).sizeBytes +1;
}
char *magic = 0;
size_t l;
getSequenceName((char*)tag, &magic, &l);
putStr(magic, l);printf(": ---------\n");
if (!im4pOnly && strncmp("IM4R", magic, l) == 0) printIM4R(tag);
if (!im4pOnly && strncmp("IM4M", magic, l) == 0) printIM4M(tag,printAll);
if (strncmp("IM4P", magic, l) == 0) printIM4P(tag);
putchar('\n');
}
error:
return;
#undef reterror
}
void printIM4R(char *buf){
#define reterror(a ...){error(a);goto error;}
char *magic;
size_t l;
getSequenceName(buf, &magic, &l);
if (strncmp("IM4R", magic, l)) reterror("unexpected \"%.*s\", expected \"IM4R\"\n",(int)l,magic);
int elems = asn1ElementsInObject(buf);
if (elems<2) reterror("expecting at least 2 elements\n");
t_asn1Tag *set = (t_asn1Tag*)asn1ElementAtIndex(buf, 1);
if (set->tagNumber != kASN1TagSET) reterror("expecting SET type\n");
set += asn1Len((char*)set+1).sizeBytes+1;
if (set->tagClass != kASN1TagClassPrivate) reterror("expecting PRIVATE type\n");
printPrivtag(asn1GetPrivateTagnum(set++,0));
printf("\n");
set += asn1Len((char*)set).sizeBytes+1;
elems = asn1ElementsInObject((char*)set);
if (elems<2) reterror("expecting at least 2 elements\n");
printI5AString((t_asn1Tag*)asn1ElementAtIndex((char*)set, 0));
printf(": ");
printHexString((t_asn1Tag*)asn1ElementAtIndex((char*)set, 1));
putchar('\n');
error:
return;
#undef reterror
}
char *getIM4PFromIMG4(char *buf){
char *magic;
size_t l;
getSequenceName(buf, &magic, &l);
if (strncmp("IMG4", magic, l)) return error("unexpected \"%.*s\", expected \"IMG4\"\n",(int)l,magic),NULL;
if (asn1ElementsInObject(buf)<2) return error("not enough elements in SEQUENCE"),NULL;
char *ret = (char*)asn1ElementAtIndex(buf, 1);
getSequenceName(ret, &magic, &l);
return (strncmp("IM4P", magic, 4) == 0) ? ret : (error("unexpected \"%.*s\", expected \"IM4P\"\n",(int)l,magic),NULL);
}
char *getIM4MFromIMG4(char *buf){
char *magic;
size_t l;
getSequenceName(buf, &magic, &l);
if (strncmp("IMG4", magic, l)) return error("unexpected \"%.*s\", expected \"IMG4\"\n",(int)l,magic),NULL;
if (asn1ElementsInObject(buf)<3) return error("not enough elements in SEQUENCE"),NULL;
char *ret = (char*)asn1ElementAtIndex(buf, 2);
if (((t_asn1Tag*)ret)->tagClass != kASN1TagClassContextSpecific) return error("unexpected Tag 0x%02x, expected SET\n",*(unsigned char*)ret),NULL;
ret += asn1Len(ret+1).sizeBytes + 1;
getSequenceName(ret, &magic, &l);
return (strncmp("IM4M", magic, 4) == 0) ? ret : NULL;
}
void printIM4M(char *buf, bool printAll){
#define reterror(a ...){error(a);goto error;}
char *magic;
size_t l;
getSequenceName(buf, &magic, &l);
if (strncmp("IM4M", magic, l)) reterror("unexpected \"%.*s\", expected \"IM4M\"\n",(int)l,magic);
int elems = asn1ElementsInObject(buf);
if (elems<2) reterror("expecting at least 2 elements\n");
if (--elems>0) {
printf("Version: ");
printNumber((t_asn1Tag*)asn1ElementAtIndex(buf, 1));
putchar('\n');
}
if (--elems>0) {
t_asn1Tag *manbset = (t_asn1Tag*)asn1ElementAtIndex(buf, 2);
if (manbset->tagNumber != kASN1TagSET) reterror("expecting SET\n");
t_asn1Tag *privtag = manbset + asn1Len((char*)manbset+1).sizeBytes+1;
size_t sb;
printPrivtag(asn1GetPrivateTagnum(privtag++,&sb));
printf("\n");
char *manbseq = (char*)privtag+sb;
manbseq+= asn1Len(manbseq).sizeBytes+1;
printMANB(manbseq, printAll);
if (!printAll) return;
}
error:
return;
#undef reterror
}
void asn1PrintValue(t_asn1Tag *tag){
if (tag->tagNumber == kASN1TagIA5String){
printI5AString(tag);
}else if (tag->tagNumber == kASN1TagOCTET){
printHexString(tag);
}else if (tag->tagNumber == kASN1TagINTEGER){
t_asn1ElemLen len = asn1Len((char*)tag+1);
unsigned char *num = (unsigned char*)tag+1 + len.sizeBytes;
uint64_t pnum = 0;
while (len.dataLen--) {
pnum *=0x100;
pnum += *num++;
//ungly workaround for WIN32
if (sizeof(uint64_t) != 8) printf("%02x",num[-1]);
}
if (sizeof(uint64_t) == 8) printf("%llu",pnum);
else printf(" (hex)");
}else if (tag->tagNumber == kASN1TagBOOLEAN){
printf("%s",(*(char*)tag+2 == 0) ? "false" : "true");
}else{
error("can't print unknown tag %02x\n",*(unsigned char*)tag);
}
}
void asn1PrintRecKeyVal(char *buf){
if (((t_asn1Tag*)buf)->tagNumber == kASN1TagSEQUENCE) {
int i;
if ((i = asn1ElementsInObject(buf)) != 2){
error("expecting 2 elements found %d\n",i);
return;
}
printI5AString((t_asn1Tag*)asn1ElementAtIndex(buf, 0));
printf(": ");
asn1PrintRecKeyVal(asn1ElementAtIndex(buf, 1));
printf("\n");
return;
}else if (((t_asn1Tag*)buf)->tagNumber != kASN1TagSET){
asn1PrintValue((t_asn1Tag *)buf);
return;
}
//must be a SET
printf("------------------------------\n");
for (int i = 0; i<asn1ElementsInObject(buf); i++) {
char *elem = (char*)asn1ElementAtIndex(buf, i);
size_t sb;
printPrivtag(asn1GetPrivateTagnum((t_asn1Tag*)elem,&sb));
printf(": ");
elem+=sb;
elem += asn1Len(elem+1).sizeBytes;
asn1PrintRecKeyVal(elem);
}
}
void printMANB(char *buf, bool printAll){
#define reterror(a ...){error(a);goto error;}
char *magic;
size_t l;
getSequenceName(buf, &magic, &l);
if (strncmp("MANB", magic, l)) reterror("unexpected \"%.*s\", expected \"MANB\"\n",(int)l,magic);
int manbElemsCnt = asn1ElementsInObject(buf);
if (manbElemsCnt<2) reterror("not enough elements in MANB\n");
char *manbSeq = (char*)asn1ElementAtIndex(buf, 1);
for (int i=0; i<asn1ElementsInObject(manbSeq); i++) {
t_asn1Tag *manbElem = (t_asn1Tag*)asn1ElementAtIndex(manbSeq, i);
size_t privTag = 0;
if (*(char*)manbElem == kASN1TagPrivate) {
size_t sb;
printPrivtag(privTag = asn1GetPrivateTagnum(manbElem,&sb));
printf(": ");
manbElem+=sb;
}else manbElem++;
manbElem += asn1Len((char*)manbElem).sizeBytes;
asn1PrintRecKeyVal((char*)manbElem);
if (!printAll && strncmp((char*)&privTag, "PNAM", 4) == 0){
break;
}
}
error:
return;
#undef reterror
}
char *getSHA1ofSqeuence(char * buf){
if (((t_asn1Tag*)buf)->tagNumber != kASN1TagSEQUENCE){
error("tag not seuqnece");
return 0;
}
t_asn1ElemLen bLen = asn1Len(buf+1);
size_t buflen = 1 + bLen.dataLen + bLen.sizeBytes;
char *ret = malloc(SHA_DIGEST_LENGTH);
if (ret)
SHA1((unsigned char*)buf, (unsigned int)buflen, (unsigned char *)ret);
return ret;
}
*/
/*int hasBuildidentityElementWithHash(plist_t identity, char *hash, uint64_t hashSize){*/
/*#define reterror(a ...){rt=0;error(a);goto error;}*/
/*#define skipelem(e) if (strcmp(key, e) == 0) {[>warning("skipping element=%s\n",key);<]goto skip;} //seems to work as it is, we don't need to see that warning anymore*/
/*int rt = 0;*/
/*plist_dict_iter dictIterator = NULL;*/
/*plist_t manifest = plist_dict_get_item(identity, "Manifest");*/
/*if (!manifest)*/
/*reterror("can't find Manifest\n");*/
/*plist_t node = NULL;*/
/*char *key = NULL;*/
/*plist_dict_new_iter(manifest, &dictIterator);*/
/*plist_dict_next_item(manifest, dictIterator, &key, &node);*/
/*do {*/
/*skipelem("BasebandFirmware")*/
/*skipelem("ftap")*/
/*skipelem("ftsp")*/
/*skipelem("rfta")*/
/*skipelem("rfts")*/
/*skipelem("SE,Bootloader")*/
/*skipelem("SE,Firmware")*/
/*skipelem("SE,MigrationOS")*/
/*skipelem("SE,OS")*/
/*skipelem("SE,UpdatePayload")*/
/*plist_t digest = plist_dict_get_item(node, "Digest");*/
/*if (!digest || plist_get_node_type(digest) != PLIST_DATA)*/
/*reterror("can't find digest for key=%s\n",key);*/
/*char *dgstData = NULL;*/
/*uint64_t len = 0;*/
/*plist_get_data_val(digest, &dgstData, &len);*/
/*if (!dgstData)*/
/*reterror("can't get dgstData for key=%s.\n",key);*/
/*if (len == hashSize && memcmp(dgstData, hash, len) == 0)*/
/*rt = 1;*/
/*free(dgstData);*/
/*skip:*/
/*plist_dict_next_item(manifest, dictIterator, &key, &node);*/
/*} while (!rt && node);*/
/*error:*/
/*free(dictIterator),dictIterator = NULL;*/
/*return rt;*/
/*#undef skipelem*/
/*#undef reterror*/
/*}*/
/*plist_t findAnyBuildidentityForFilehash(plist_t identities, char *hash, uint64_t hashSize){*/
/*#define skipelem(e) if (strcmp(key, e) == 0) {[>warning("skipping element=%s\n",key);<]goto skip;} //seems to work as it is, we don't need to see that warning anymore*/
/*#define reterror(a ...){rt=NULL;error(a);goto error;}*/
/*plist_t rt = NULL;*/
/*plist_dict_iter dictIterator = NULL;*/
/*for (int i=0; !rt && i<plist_array_get_size(identities); i++) {*/
/*plist_t idi = plist_array_get_item(identities, i);*/
/*plist_t manifest = plist_dict_get_item(idi, "Manifest");*/
/*if (!manifest)*/
/*reterror("can't find Manifest. i=%d\n",i);*/
/*plist_t node = NULL;*/
/*char *key = NULL;*/
/*plist_dict_new_iter(manifest, &dictIterator);*/
/*plist_dict_next_item(manifest, dictIterator, &key, &node);*/
/*do {*/
/*skipelem("BasebandFirmware")*/
/*skipelem("ftap")*/
/*skipelem("ftsp")*/
/*skipelem("rfta")*/
/*skipelem("rfts")*/
/*plist_t digest = plist_dict_get_item(node, "Digest");*/
/*if (!digest || plist_get_node_type(digest) != PLIST_DATA)*/
/*reterror("can't find digest for key=%s. i=%d\n",key,i);*/
/*char *dgstData = NULL;*/
/*uint64_t len = 0;*/
/*plist_get_data_val(digest, &dgstData, &len);*/
/*if (!dgstData)*/
/*reterror("can't get dgstData for key=%s. i=%d\n",key,i);*/
/*if (len == hashSize && memcmp(dgstData, hash, len) == 0)*/
/*rt = idi;*/
/*free(dgstData);*/
/*skip:*/
/*plist_dict_next_item(manifest, dictIterator, &key, &node);*/
/*} while (!rt && node);*/
/*free(dictIterator),dictIterator = NULL;*/
/*}*/
/*error:*/
/*if (dictIterator) free(dictIterator);*/
/*return rt;*/
/*#undef reterror*/
/*#undef skipelem*/
/*}*/
/*int doForDGSTinIM4M(const char *im4m, void *state, int (*loop_cb)(char elemNameStr[4], char *dgstData, size_t dgstDataLen, void *state)){*/
/*int err = 0;*/
/*#define reterror(code, msg ...) do {error(msg);err=code;goto error;}while(0)*/
/*if (!sequenceHasName(im4m, "IM4M"))*/
/*reterror(-1,"can't find IM4M tag\n");*/
/*char *im4mset = (char *)asn1ElementAtIndex(im4m, 2);*/
/*if (!im4mset)*/
/*reterror(-2,"can't find im4mset\n");*/
/*char *manbSeq = getValueForTagInSet(im4mset, *(uint32_t*)"BNAM");*/
/*if (!manbSeq)*/
/*reterror(-3,"can't find manbSeq\n");*/
/*char *manbSet = (char*)asn1ElementAtIndex(manbSeq, 1);*/
/*if (!manbSet)*/
/*reterror(-4,"can't find manbSet\n");*/
/*for (int i=0; i<asn1ElementsInObject(manbSet); i++) {*/
/*char *curr = asn1ElementAtIndex(manbSet, i);*/
/*size_t sb;*/
/*if (asn1GetPrivateTagnum((t_asn1Tag*)curr, &sb) == *(uint32_t*)"PNAM")*/
/*continue;*/
/*char *cSeq = (char*)curr+sb;*/
/*cSeq += asn1Len(cSeq).sizeBytes;*/
/*char *elemName = asn1ElementAtIndex(cSeq, 0);*/
/*t_asn1ElemLen elemNameLen = asn1Len(elemName+1);*/
/*char *elemNameStr = elemName + elemNameLen.sizeBytes+1;*/
/*char *elemSet = (char*)asn1ElementAtIndex(cSeq, 1);*/
/*if (!elemSet)*/
/*reterror(-5, "can't find elemSet. i=%d\n",i);*/
/*char *dgstSeq = getValueForTagInSet(elemSet, *(uint32_t*)"TSGD");*/
/*if (!dgstSeq)*/
/*reterror(-6, "can't find dgstSeq. i=%d\n",i);*/
/*char *dgst = asn1ElementAtIndex(dgstSeq, 1);*/
/*if (!dgst || asn1Tag(dgst)->tagNumber != kASN1TagOCTET)*/
/*reterror(-7, "can't find DGST. i=%d\n",i);*/
/*t_asn1ElemLen lenDGST = asn1Len((char*)dgst+1);*/
/*char *dgstData = (char*)dgst+lenDGST.sizeBytes+1;*/
/*if ((err = loop_cb(elemNameStr, dgstData, lenDGST.dataLen, state))){*/
/*if (err > 0){ //restart loop if err > 0*/
/*i = -1;*/
/*err = 0;*/
/*continue;*/
/*}*/
/*break;*/
/*}*/
/*}*/
/*error:*/
/*return err;*/
/*#undef reterror*/
/*}*/
/*int im4m_buildidentity_check_cb(char elemNameStr[4], char *dgstData, size_t dgstDataLen, struct {plist_t rt; plist_t identities;} *state){*/
/*#define skipelem(e) if (strncmp(e, elemNameStr,4) == 0) return 0*/
/*skipelem("ftsp");*/
/*skipelem("ftap");*/
/*skipelem("rfta");*/
/*skipelem("rfts");*/
/*if (state->rt){*/
/*if (!hasBuildidentityElementWithHash(state->rt, dgstData, dgstDataLen)){*/
/*//remove identity we are not looking for and start comparing all hashes again*/
/*plist_array_remove_item(state->identities, plist_array_get_item_index(state->rt));*/
/*state->rt = NULL;*/
/*return 1; //trigger loop restart*/
/*}*/
/*}else{*/
/*if (!(state->rt = findAnyBuildidentityForFilehash(state->identities, dgstData, dgstDataLen)))*/
/*return (error("can't find any identity which matches all hashes inside IM4M\n"),-1);*/
/*}*/
/*#undef skipelem*/
/*return 0;*/
/*}*/
/*plist_t getBuildIdentityForIM4M(const char *buf, const plist_t buildmanifest){*/
/*#define reterror(a ...){state.rt=NULL;error(a);goto error;}*/
/*#define skipelem(e) if (strncmp(elemNameStr, e, 4) == 0) {[>warning("skipping element=%s\n",e);<]continue;} //seems to work as it is, we don't need to see that warning anymore*/
/*plist_t manifest = plist_copy(buildmanifest);*/
/*struct {plist_t rt; plist_t identities;} state;*/
/*state.rt = NULL;*/
/*state.identities = plist_dict_get_item(manifest, "BuildIdentities");*/
/*if (!state.identities)*/
/*reterror("can't find BuildIdentities\n");*/
/*doForDGSTinIM4M(buf, (void*)&state, (int (*)(char[4], char *, size_t, void *))im4m_buildidentity_check_cb);*/
/*plist_t finfo = plist_dict_get_item(state.rt, "Info");*/
/*plist_t fdevclass = plist_dict_get_item(finfo, "DeviceClass");*/
/*plist_t fresbeh = plist_dict_get_item(finfo, "RestoreBehavior");*/
/*if (!finfo || !fdevclass || !fresbeh)*/
/*reterror("found buildidentiy, but can't read information\n");*/
/*plist_t origIdentities = plist_dict_get_item(buildmanifest, "BuildIdentities");*/
/*for (int i=0; i<plist_array_get_size(origIdentities); i++) {*/
/*plist_t curr = plist_array_get_item(origIdentities, i);*/
/*plist_t cinfo = plist_dict_get_item(curr, "Info");*/
/*plist_t cdevclass = plist_dict_get_item(cinfo, "DeviceClass");*/
/*plist_t cresbeh = plist_dict_get_item(cinfo, "RestoreBehavior");*/
/*if (plist_compare_node_value(cresbeh, fresbeh) && plist_compare_node_value(cdevclass, fdevclass)) {*/
/*state.rt = curr;*/
/*goto error;*/
/*}*/
/*}*/
/*//fails if loop ended without jumping to error*/
/*reterror("found indentity, but failed to match it with orig copy\n");*/
/*error:*/
/*plist_free(manifest);*/
/*return state.rt;*/
/*#undef reterror*/
/*}*/
/*void printGeneralBuildIdentityInformation(plist_t buildidentity){*/
/*plist_t info = plist_dict_get_item(buildidentity, "Info");*/
/*plist_dict_iter iter = NULL;*/
/*plist_dict_new_iter(info, &iter);*/
/*plist_type t;*/
/*plist_t node = NULL;*/
/*char *key = NULL;*/
/*while (plist_dict_next_item(info, iter, &key, &node),node) {*/
/*char *str = NULL;*/
/*switch (t = plist_get_node_type(node)) {*/
/*case PLIST_STRING:*/
/*plist_get_string_val(node, &str);*/
/*printf("%s : %s\n",key,str);*/
/*break;*/
/*case PLIST_BOOLEAN:*/
/*plist_get_bool_val(node, (uint8_t*)&t);*/
/*printf("%s : %s\n",key,((uint8_t)t) ? "YES" : "NO" );*/
/*default:*/
/*break;*/
/*}*/
/*if (str) free(str);*/
/*}*/
/*if (iter) free(iter);*/
/*}*/
/*int verify_signature(char *data, char *sig, char *certificate, int useSHA384){*/
/*#ifndef IMG4TOOL_NOOPENSSL*/
/*//return 0 if signature valid, 1 if invalid, <0 if error occured*/
/*int err = 0;*/
/*EVP_MD_CTX *mdctx = NULL;*/
/*#define reterror(a ...){err=1;error(a);goto error;}*/
/*t_asn1ElemLen dataSize = asn1Len(data+1);*/
/*t_asn1ElemLen sigSize = asn1Len(sig+1);*/
/*t_asn1ElemLen certSize = asn1Len(certificate+1);*/
/*X509 *cert = d2i_X509(NULL, (const unsigned char**)&certificate, certSize.dataLen + certSize.sizeBytes + 1);*/
/*EVP_PKEY *certpubkey = X509_get_pubkey(cert);*/
/*retassure(-1, mdctx = EVP_MD_CTX_create());*/
/*retassure(-2, EVP_DigestVerifyInit(mdctx, NULL, (useSHA384) ? EVP_sha384() : EVP_sha1(), NULL, certpubkey) == 1);*/
/*retassure(-3,EVP_DigestVerifyUpdate(mdctx, data, dataSize.dataLen + dataSize.sizeBytes +1) == 1);*/
/*err = (EVP_DigestVerifyFinal(mdctx, (unsigned char*)sig+1 + sigSize.sizeBytes, sigSize.dataLen) != 1);*/
/*error:*/
/*if(mdctx) EVP_MD_CTX_destroy(mdctx);*/
/*return err;*/
/*#undef reterror*/
/*#else*/
/*printf("[FATAL!] can't verify signature, because img4tool was compiled without openssl\n");*/
/*return 0;*/
/*#endif*/
/*}*/
/*int find_dgst_cb(char elemNameStr[4], char *dgstData, size_t dgstDataLen, void *state){*/
/*return memcmp(dgstData, state, dgstDataLen) == 0 ? -255 : 0; //-255 is not an error in this case, but indicates that we found our hash*/
/*}*/
/*int verifyIM4MSignature(const char *buf){*/
/*int err = 0;*/
/*#define reterror(code,a ...){error(a);err=code;goto error;}*/
/*retassure(-1,asn1ElementsInObject(buf) == 5);*/
/*char *im4m = asn1ElementAtIndex(buf, 2);*/
/*char *sig = asn1ElementAtIndex(buf, 3);*/
/*char *certs = asn1ElementAtIndex(buf, 4);*/
/*int elems = 0;*/
/*retassure(-2, (elems = asn1ElementsInObject(certs)) >=1); //iPhone7 has 1 cert, while pre-iPhone7 have 2 certs*/
/*// char *bootAuthority = asn1ElementAtIndex(certs, 0); //does not exist on iPhone7*/
/*char *tssAuthority = asn1ElementAtIndex(certs, elems-1); //is always last item*/
/*err = verify_signature(im4m, sig, tssAuthority, elems < 2); //use SHA384 if elems is 2 otherwise use SHA1*/
/*error:*/
/*return err;*/
/*#undef reterror*/
/*}*/
/*char *getBNCHFromIM4M(const char* im4m, size_t *nonceSize){*/
/*#define reterror(a ...){error(a);ret=NULL;goto error;}*/
/*char *ret = NULL;*/
/*char *mainSet = NULL;*/
/*char *manbSet = NULL;*/
/*char *manpSet = NULL;*/
/*char *nonceOctet = NULL;*/
/*char *bnch = NULL;*/
/*size_t bnchSize = 0;*/
/*char *manb = NULL;*/
/*char *manp = NULL;*/
/*char *certs = NULL;*/
/*if (!im4m) reterror("Got empty IM4M\n");*/
/*if (asn1ElementsInObject(im4m) != 5) {*/
/*error("unexpected number of Elements (%d) in IM4M sequence\n", asn1ElementsInObject(im4m));*/
/*goto error;*/
/*}*/
/*mainSet = asn1ElementAtIndex(im4m, 2);*/
/*certs = asn1ElementAtIndex(im4m, 4);*/
/*manb = getValueForTagInSet((char*)mainSet, *(uint32_t*)"BNAM"); //MANB priv Tag*/
/*if (asn1ElementsInObject(manb)< 2){*/
/*error("unexpected number of Elements in MANB sequence\n");*/
/*goto error;*/
/*}*/
/*manbSet = asn1ElementAtIndex(manb, 1);*/
/*manp = getValueForTagInSet((char*)manbSet, *(uint32_t*)"PNAM"); //MANP priv Tag*/
/*if (asn1ElementsInObject(manp)< 2){*/
/*error("unexpected number of Elements in MANP sequence\n");*/
/*goto error;*/
/*}*/
/*manpSet = asn1ElementAtIndex(manp, 1);*/
/*bnch = getValueForTagInSet((char*)manpSet, *(uint32_t*)"HCNB"); //BNCH priv Tag*/
/*if (asn1ElementsInObject(bnch)< 2){*/
/*error("unexpected number of Elements in BNCH sequence\n");*/
/*goto error;*/
/*}*/
/*nonceOctet = (char*)asn1ElementAtIndex(bnch, 1);*/
/*nonceOctet++;*/
/*ret = nonceOctet + asn1Len(nonceOctet).sizeBytes;*/
/*bnchSize = asn1Len(nonceOctet).dataLen;*/
/*// iPhone 7 and above use 32 byte nonce*/
/*if (bnchSize != (asn1ElementsInObject(certs) == 1 ? 32 : 20)) {*/
/*reterror("BNCH size incorrect\n");*/
/*}*/
/*if (nonceSize) *nonceSize = bnchSize;*/
/*error:*/
/*return ret;*/
/*#undef reterror*/
/*}*/
/*int verifyIMG4(char *buf, plist_t buildmanifest){*/
/*//return 0 on valid file, positive value on invalid file, negative value when errors occured*/
/*int err = 0;*/
/*#define reterror(code,a ...){error(a);err=code;goto error;}*/
/*char *im4pSHA = NULL;*/
/*if (sequenceHasName(buf, "IMG4")){*/
/*//verify IMG4*/
/*char *im4p = getIM4PFromIMG4(buf);*/
/*im4pSHA = getSHA1ofSqeuence(im4p);*/
/*if (!im4p) goto error;*/
/*buf = getElementFromIMG4(buf, "IM4M");*/
/*}*/
/*if (!sequenceHasName(buf, "IM4M"))*/
/*reterror(-1,"unable to find IM4M tag");*/
/*if (im4pSHA){*/
/*if (doForDGSTinIM4M(buf, im4pSHA, find_dgst_cb) == -255)*/
/*printf("[OK] IM4P is valid for the attached IM4M\n");*/
/*else*/
/*reterror(1,"IM4P can't be verified by IM4M\n");*/
/*}*/
/*if ((err = verifyIM4MSignature(buf))){*/
/*reterror((err < 0) ? err : 2, "Signature verification of IM4M failed with error=%d\n",err);*/
/*}else*/
/*printf("[OK] IM4M signature is verified by TssAuthority\n");*/
/*#warning TODO verify certificate chain*/
/*if (buildmanifest) {*/
/*plist_t identity = getBuildIdentityForIM4M(buf, buildmanifest);*/
/*if (identity){*/
/*printf("[OK] IM4M is valid for the given BuildManifest for the following restore:\n\n");*/
/*printGeneralBuildIdentityInformation(identity);*/
/*}else{*/
/*reterror(3,"IM4M is not valid for any restore within the Buildmanifest\n");*/
/*}*/
/*}else{*/
/*warning("No BuildManifest specified, can't verify restore type of APTicket\n");*/
/*}*/
/*error:*/
/*safeFree(im4pSHA);*/
/*return err;*/
/*#undef reterror*/
/*}*/