Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/external/source/exploits/CVE-2017-13861/liboffsetfinder64/img4.c
Views: 11784
//1// img4.c2// img4tool3//4// Created by tihmstar on 15.06.16.5// Copyright © 2016 tihmstar. All rights reserved.6//78#include "img4.h"9#include "all_img4tool.h"10#include <stdlib.h>11#include <string.h>12#include <sys/types.h>13#include <stdint.h>14#include "lzssdec.h"1516/*#ifndef IMG4TOOL_NOLZFSE*/17/*#ifdef HAVE_LIBLZFSE*/18/*# include <lzfse.h>*/19/*#elif defined(HAVE_LIBCOMPRESSION)*/20/*# include <compression.h>*/21/*# define lzfse_decode_buffer(src, src_size, dst, dst_size, scratch) \*/22/*compression_decode_buffer(src, src_size, dst, dst_size, scratch, COMPRESSION_LZFSE)*/23/*#else*/24/*# error "either liblzfse or libcompression is needed"*/25/*#endif*/26/*#endif*/2728/*#ifndef IMG4TOOL_NOOPENSSL*/29/*# include <openssl/x509.h>*/30/*# include <openssl/evp.h>*/31/*# include <openssl/sha.h>*/32/*#elif defined(__APPLE__)*/33/*# include <CommonCrypto/CommonDigest.h>*/34/*# define SHA1(d, n, md) CC_SHA1(d, n, md)*/35/*# define SHA_DIGEST_LENGTH CC_SHA1_DIGEST_LENGTH*/36/*#else*/37/*# error openssl is required on non-Apple*/38/*#endif*/3940#define safeFree(buf) if (buf) free(buf), buf = NULL41#define assure(a) do{ if ((a) == 0){err=1; goto error;} }while(0)42#define retassure(retcode, a) do{ if ((a) == 0){err=retcode; goto error;} }while(0)43#define asn1Tag(a) ((t_asn1Tag*)a)4445t_asn1ElemLen asn1Len(const char buf[4]){46t_asn1Length *sTmp = (t_asn1Length *)buf;47size_t outSize = 0;48int sizeBytes_ = 0;4950unsigned char *sbuf = (unsigned char *)buf;5152if (!sTmp->isLong) outSize = sTmp->len;53else{54sizeBytes_ = sTmp->len;55for (int i=0; i<sizeBytes_; i++) {56outSize *= 0x100;57outSize += sbuf[1+i];58}59}6061t_asn1ElemLen ret;62ret.dataLen = outSize;63ret.sizeBytes = sizeBytes_+1;64return ret;65}6667char *ans1GetString(char *buf, char **outString, size_t *strlen){6869t_asn1Tag *tag = (t_asn1Tag *)buf;7071if (!(tag->tagNumber | kASN1TagIA5String)) {72error("not a string\n");73return 0;74}7576t_asn1ElemLen len = asn1Len(++buf);77*strlen = len.dataLen;78buf+=len.sizeBytes;79if (outString) *outString = buf;8081return buf+*strlen;82}8384int asn1ElementAtIndexWithCounter(const char *buf, int index, t_asn1Tag **tagret){85int ret = 0;8687if (!((t_asn1Tag *)buf)->isConstructed) return 0;88t_asn1ElemLen len = asn1Len(++buf);8990buf +=len.sizeBytes;9192while (len.dataLen) {93if (ret == index && tagret){94*tagret = (t_asn1Tag*)buf;95return ret;96}9798if (*buf == kASN1TagPrivate) {99size_t sb;100asn1GetPrivateTagnum((t_asn1Tag*)buf,&sb);101buf+=sb;102len.dataLen-=sb;103}else if (*buf == (char)0x9F){104//buf is element in set and it's value is encoded in the next byte105t_asn1ElemLen l = asn1Len(++buf);106if (l.sizeBytes > 1) l.dataLen += 0x80;107buf += l.sizeBytes;108len.dataLen -= 1 + l.sizeBytes;109}else110buf++,len.dataLen--;111112t_asn1ElemLen sublen = asn1Len(buf);113size_t toadd =sublen.dataLen + sublen.sizeBytes;114len.dataLen -=toadd;115buf +=toadd;116ret ++;117}118119return ret;120}121122int asn1ElementsInObject(const char *buf){123return asn1ElementAtIndexWithCounter(buf, -1, NULL);124}125126char *asn1ElementAtIndex(const char *buf, int index){127t_asn1Tag *ret;128asn1ElementAtIndexWithCounter(buf, index, &ret);129return (char*)ret;130}131132int getSequenceName(const char *buf,char**name, size_t *nameLen){133#define reterror(a ...){error(a); err = -1; goto error;}134int err = 0;135if (((t_asn1Tag*)buf)->tagNumber != kASN1TagSEQUENCE) reterror("not a SEQUENCE\n");136int elems = asn1ElementsInObject(buf);137if (!elems) reterror("no elements in SEQUENCE\n");138size_t len;139ans1GetString((char*)asn1ElementAtIndex(buf,0),name,&len);140if (nameLen) *nameLen = len;141error:142return err;143#undef reterror144}145146size_t asn1GetPrivateTagnum(t_asn1Tag *tag, size_t *sizebytes){147if (*(unsigned char*)tag != 0xff) {148error("not a private TAG 0x%02x\n",*(unsigned int*)tag);149return 0;150}151size_t sb = 1;152t_asn1ElemLen taglen = asn1Len((char*)++tag);153taglen.sizeBytes-=1;154if (taglen.sizeBytes != 4){155/*156WARNING: seems like apple's private tag is always 4 bytes long157i first assumed 0x84 can be parsed as long size with 4 bytes,158but 0x86 also seems to be 4 bytes size even if one would assume it means 6 bytes size.159This opens the question what the 4 or 6 nibble means.160*/161taglen.sizeBytes = 4;162}163size_t tagname =0;164do {165tagname *=0x100;166tagname>>=1;167tagname += ((t_asn1PrivateTag*)tag)->num;168sb++;169} while (((t_asn1PrivateTag*)tag++)->more);170if (sizebytes) *sizebytes = sb;171return tagname;172}173174uint64_t ans1GetNumberFromTag(t_asn1Tag *tag){175if (tag->tagNumber != kASN1TagINTEGER) return (error("not an INTEGER\n"),0);176uint64_t ret = 0;177t_asn1ElemLen len = asn1Len((char*)++tag);178unsigned char *data = (unsigned char*)tag+len.sizeBytes;179while (len.dataLen--) {180ret *= 0x100;181ret+= *data++;182}183184return ret;185}186187void printStringWithKey(char*key, t_asn1Tag *string){188char *str = 0;189size_t strlen;190ans1GetString((char*)string,&str,&strlen);191printf("%s",key);192putStr(str, strlen);193putchar('\n');194}195196void printPrivtag(size_t privTag){197char *ptag = (char*)&privTag;198int len = 0;199while (*ptag) ptag++,len++;200while (len--) putchar(*--ptag);201}202203void printHexString(t_asn1Tag *str){204if (str->tagNumber != kASN1TagOCTET){205error("not an OCTET string\n");206return;207}208209t_asn1ElemLen len = asn1Len((char*)str+1);210211unsigned char *string = (unsigned char*)str + len.sizeBytes +1;212213while (len.dataLen--) printf("%02x",*string++);214}215216void printI5AString(t_asn1Tag *str){217if (str->tagNumber != kASN1TagIA5String){218error("not an I5A string\n");219return;220}221222t_asn1ElemLen len = asn1Len((char*)++str);223putStr(((char*)str)+len.sizeBytes, len.dataLen);224}225226void printKBAGOctet(char *octet){227#define reterror(a ...){error(a);goto error;}228if (((t_asn1Tag*)octet)->tagNumber != kASN1TagOCTET) reterror("not an OCTET\n");229230t_asn1ElemLen octetlen = asn1Len(++octet);231octet +=octetlen.sizeBytes;232//main seq233int subseqs = asn1ElementsInObject(octet);234for (int i=0; i<subseqs; i++) {235char *s = (char*)asn1ElementAtIndex(octet, i);236int elems = asn1ElementsInObject(s);237238if (elems--){239//integer (currently unknown?)240t_asn1Tag *num = (t_asn1Tag*)asn1ElementAtIndex(s, 0);241if (num->tagNumber != kASN1TagINTEGER) warning("skipping unexpected tag\n");242else{243char n = *(char*)(num+2);244printf("num: %d\n",n);245}246}247if (elems--)printHexString((t_asn1Tag*)asn1ElementAtIndex(s, 1)),putchar('\n');248if (elems--)printHexString((t_asn1Tag*)asn1ElementAtIndex(s, 2)),putchar('\n');249}250251error:252return;253#undef reterror254}255256void printNumber(t_asn1Tag *tag){257if (tag->tagNumber != kASN1TagINTEGER) {258error("tag not an INTEGER\n");259return;260}261t_asn1ElemLen len = asn1Len((char*)++tag);262uint32_t num = 0;263while (len.sizeBytes--) {264num *=0x100;265num += *(unsigned char*)++tag;266}267printf("%u",num);268}269270void printIM4P(char *buf){271#define reterror(a ...){error(a);goto error;}272273char *magic;274size_t l;275getSequenceName(buf, &magic, &l);276if (strncmp("IM4P", magic, l)) reterror("unexpected \"%.*s\", expected \"IM4P\"\n",(int)l,magic);277278int elems = asn1ElementsInObject(buf);279if (--elems>0) printStringWithKey("type: ",(t_asn1Tag*)asn1ElementAtIndex(buf, 1));280if (--elems>0) printStringWithKey("desc: ",(t_asn1Tag*)asn1ElementAtIndex(buf, 2));281if (--elems>0) {282//data283t_asn1Tag *data = (t_asn1Tag*)asn1ElementAtIndex(buf, 3);284if (data->tagNumber != kASN1TagOCTET) warning("skipped an unexpected tag where OCTETSTING was expected\n");285else printf("size: 0x%08zx\n",asn1Len((char*)data+1).dataLen);286}287if (--elems>0) {288//kbag values289printf("\nKBAG\n");290printKBAGOctet((char*)asn1ElementAtIndex(buf, 4));291}else{292printf("\nIM4P does not contain KBAG values\n");293}294295error:296return;297#undef reterror298}299300char* extractPayloadFromIM4P(const char* buf, const char** compname, size_t *len) {301int elems = asn1ElementsInObject(buf);302if (elems < 4) {303error("not enough elements in SEQUENCE: %d", elems);304return NULL;305}306307char *name = NULL;308size_t namelen = 0;309char *krnl_tag = asn1ElementAtIndex(buf, 1);310char *rv = ans1GetString(krnl_tag, &name, &namelen);311312if (rv == NULL || namelen != 4 || strncmp(name, "krnl", 4) != 0) {313printf("Not a krnl\n");314return NULL;315}316317char *dataTag = asn1ElementAtIndex(buf, 3)+1;318t_asn1ElemLen dlen = asn1Len(dataTag);319char *data = dataTag+dlen.sizeBytes;320321char *kernel = NULL;322const char* comp = NULL;323324if (strncmp(data, "complzss", 8) == 0) {325comp = "lzss";326kernel = tryLZSS(data, len);327} else if (strncmp(data, "bvx2", 4) == 0) {328comp = "lzfse";329#ifndef IMG4TOOL_NOLZFSE330char *compTag = data + dlen.dataLen;331char *fakeCompSizeTag = asn1ElementAtIndex(compTag, 0);332char *uncompSizeTag = asn1ElementAtIndex(compTag, 1);333334size_t fake_src_size = ans1GetNumberFromTag(asn1Tag(fakeCompSizeTag));335size_t dst_size = ans1GetNumberFromTag(asn1Tag(uncompSizeTag));336337size_t src_size = dlen.dataLen;338339if (fake_src_size != 1) {340printf("fake_src_size not 1 but 0x%zx!\n", fake_src_size);341}342343kernel = malloc(dst_size);344345size_t uncomp_size = lzfse_decode_buffer(346(uint8_t*) kernel, dst_size,347(uint8_t*) data, src_size,348NULL);349350if (uncomp_size != dst_size) {351printf("expected to decompress %zu bytes but only got %zu\n", dst_size, uncomp_size);352free(kernel);353kernel = NULL;354} else {355*len = dst_size;356}357#else358printf("Can't unpack data because img4tool was compiled without lzfse!\n");359#endif360}361362*compname = comp;363return kernel;364}365366/*367int extractFileFromIM4P(char *buf, const char *dstFilename){368int elems = asn1ElementsInObject(buf);369if (elems < 4){370error("not enough elements in SEQUENCE %d\n",elems);371return -2;372}373374375char *dataTag = asn1ElementAtIndex(buf, 3)+1;376t_asn1ElemLen dlen = asn1Len(dataTag);377char *data = dataTag+dlen.sizeBytes;378379char* kernel = NULL;380{381size_t kernel_len = 0;382const char* compname = NULL;383kernel = extractPayloadFromIM4P(buf, &compname, &kernel_len);384385if (compname != NULL) {386printf("Kernelcache detected, uncompressing (%s): %s\n", compname, kernel ? "ok" : "failure");387}388389if (kernel != NULL) {390data = kernel;391dlen.dataLen = kernel_len;392}393}394395FILE *f = fopen(dstFilename, "wb");396if (!f) {397error("can't open file %s\n",dstFilename);398return -1;399}400fwrite(data, dlen.dataLen, 1, f);401fclose(f);402403if (kernel)404free(kernel);405406return 0;407}408*/409410int sequenceHasName(const char *buf, char *name){411char *magic;412size_t l;413int err = getSequenceName(buf, &magic, &l);414return !err && strncmp(name, magic, l) == 0;415}416417char *getElementFromIMG4(char *buf, char* element){418#define reterror(a ...) return (error(a),NULL)419if (!sequenceHasName(buf, "IMG4")) reterror("not img4 sequcence\n");420421int elems = asn1ElementsInObject(buf);422for (int i=0; i<elems; i++) {423424char *elemen = asn1ElementAtIndex(buf, i);425426if (asn1Tag(elemen)->tagNumber != kASN1TagSEQUENCE && asn1Tag(elemen)->tagClass == kASN1TagClassContextSpecific) {427//assuming we found a "subcontainer"428elemen += asn1Len((char*)elemen+1).sizeBytes+1;429}430431if (asn1Tag(elemen)->tagNumber == kASN1TagSEQUENCE && sequenceHasName(elemen, element)) {432return (char*)elemen;433}434}435reterror("element %s not found in IMG4\n",element);436#undef reterror437}438439int extractElementFromIMG4(char *buf, char* element, const char *dstFilename){440#define reterror(a ...) return (error(a),-1)441442char *elemen = getElementFromIMG4(buf, element);443if (!elemen) return -1;444FILE *f = fopen(dstFilename, "wb");445if (!f) {446error("can't open file %s\n",dstFilename);447return -1;448}449450t_asn1ElemLen len = asn1Len((char*)elemen+1);451size_t flen = len.dataLen + len.sizeBytes +1;452fwrite(elemen, flen, 1, f);453fclose(f);454455return 0;456#undef reterror457}458459int asn1MakeSize(char *sizeBytesDst, size_t size){460int off = 0;461if (size >= 0x1000000) {462// 1+4 bytes length463sizeBytesDst[off++] = 0x84;464sizeBytesDst[off++] = (size >> 24) & 0xFF;465sizeBytesDst[off++] = (size >> 16) & 0xFF;466sizeBytesDst[off++] = (size >> 8) & 0xFF;467sizeBytesDst[off++] = size & 0xFF;468} else if (size >= 0x10000) {469// 1+3 bytes length470sizeBytesDst[off++] = 0x83;471sizeBytesDst[off++] = (size >> 16) & 0xFF;472sizeBytesDst[off++] = (size >> 8) & 0xFF;473sizeBytesDst[off++] = size & 0xFF;474} else if (size >= 0x100) {475// 1+2 bytes length476sizeBytesDst[off++] = 0x82;477sizeBytesDst[off++] = (size >> 8) & 0xFF;478sizeBytesDst[off++] = (size & 0xFF);479} else if (size >= 0x80) {480// 1+1 byte length481sizeBytesDst[off++] = 0x81;482sizeBytesDst[off++] = (size & 0xFF);483} else {484// 1 byte length485sizeBytesDst[off++] = size & 0xFF;486}487return off;488}489490char *asn1PrepandTag(char *buf, t_asn1Tag tag){491t_asn1ElemLen len = asn1Len(buf+1);492493//alloc mem for oldTag+oldSizebytes+oldData + newTag + newTagSizebytesMax494char *ret = malloc(len.sizeBytes + len.dataLen +1 +1+4);495ret[0] = *(char*)&tag;496int nSizeBytes = asn1MakeSize(ret+1, len.sizeBytes + len.dataLen +1);497memcpy(ret + nSizeBytes+1, buf, len.sizeBytes + len.dataLen +1);498return ret;499}500501char *asn1AppendToTag(char *buf, char *toappend){502t_asn1ElemLen buflen = asn1Len(buf+1);503t_asn1ElemLen apndLen = asn1Len(toappend+1);504505//alloc memory for bufdata + buftag + apndData + apndSizebytes + apndTag + maxSizeBytesForBuf506size_t containerLen;507char *ret = malloc(1 +(containerLen = buflen.dataLen +apndLen.sizeBytes + apndLen.dataLen +1) +4);508509ret[0] = buf[0];510int nSizeBytes = asn1MakeSize(ret+1, containerLen);511//copy old data512memcpy(ret + nSizeBytes+1, buf+1+buflen.sizeBytes, buflen.dataLen);513514515memcpy(ret +nSizeBytes+1+ buflen.dataLen, toappend, apndLen.sizeBytes +apndLen.dataLen +1);516free(buf);517518return ret;519}520521char *makeIM4RWithNonce(char *nonce){522char template[] = {0xA1, 0x23, 0x30, 0x21, 0x16, 0x04, 0x49, 0x4D,5230x34, 0x52, 0x31, 0x19, 0xFF, 0x84, 0x92, 0xB9,5240x86, 0x4E, 0x12, 0x30, 0x10, 0x16, 0x04, 0x42,5250x4E, 0x43, 0x4E, 0x04, 0x08};526char *ret = malloc(sizeof(template)+8);527strncpy(ret, template,sizeof(template));528strncpy(ret+sizeof(template), nonce, 8);529return ret;530}531/*532533char *makeIMG4(char *im4p, char *im4m, char *im4r, size_t *size){534t_asn1Tag elem0;535elem0.tagNumber = 0;536elem0.tagClass = kASN1TagClassContextSpecific;537elem0.isConstructed = 1;538if (im4m) im4m = asn1PrepandTag(im4m, elem0);539540char *sequence = malloc(2);541sequence[0] = 0x30;542sequence[1] = 0x00;543544char iA5String_IMG4[] = {0x16, 0x04, 0x49, 0x4D, 0x47, 0x34};545546sequence = asn1AppendToTag(sequence, iA5String_IMG4);547if (im4p) sequence = asn1AppendToTag(sequence, im4p);548if (im4m) sequence = asn1AppendToTag(sequence, im4m);549if (im4r) {550char *noncebuf = makeIM4RWithNonce(im4r);551sequence = asn1AppendToTag(sequence, noncebuf);552free(noncebuf);553}554555if (size){556t_asn1ElemLen retlen = asn1Len(sequence+1);557*size = 1+ retlen.dataLen + retlen.sizeBytes;558}559free(im4m); //only freeing local copy, not actually freeing outside im4m buffer560561return sequence;562}563564int replaceNameInIM4P(char *buf, const char *newName){565566if (asn1ElementsInObject(buf)<2){567error("not enough objects in sequence\n");568return -1;569}570571char *nameTag = asn1ElementAtIndex(buf, 1);572573if (asn1Tag(nameTag)->tagNumber != kASN1TagIA5String){574error("nameTag is not IA5String\n");575return -2;576}577t_asn1ElemLen len;578if ((len = asn1Len(nameTag+1)).dataLen !=4){579error("nameTag has not a length of 4 Bytes, actual len=%ld\n",len.dataLen);580return -2;581}582583memmove(nameTag + 1 + len.sizeBytes, newName, 4);584585return 0;586}587588589char *getValueForTagInSet(char *set, uint32_t tag){590#define reterror(a) return (error(a),NULL)591592if (((t_asn1Tag*)set)->tagNumber != kASN1TagSET) reterror("not a SET\n");593t_asn1ElemLen setlen = asn1Len(++set);594595for (char *setelems = set+setlen.sizeBytes; setelems<set+setlen.dataLen;) {596597if (*(unsigned char*)setelems == 0xff) {598//priv tag599size_t sb;600size_t ptag = asn1GetPrivateTagnum((t_asn1Tag*)setelems,&sb);601setelems += sb;602t_asn1ElemLen len = asn1Len(setelems);603setelems += len.sizeBytes;604if (tag == ptag) return setelems;605setelems +=len.dataLen;606}else{607//normal tag608t_asn1ElemLen len = asn1Len(setelems);609setelems += len.sizeBytes + 1;610if (((t_asn1Tag*)setelems)->tagNumber == tag) return setelems;611setelems += len.dataLen;612}613}614return 0;615#undef reterror616}617618void printElemsInIMG4(char *buf, bool printAll, bool im4pOnly){619#define reterror(a...) {error(a); goto error;}620char *magic;621size_t l;622getSequenceName(buf, &magic, &l);623if (strncmp("IMG4", magic, l)) reterror("unexpected \"%.*s\", expected \"IMG4\"\n",(int)l,magic);624printf("IMG4:\n");625int elems = asn1ElementsInObject(buf);626627for (int i=1; i<elems; i++) {628char *tag = (char*)asn1ElementAtIndex(buf, i);629630if (((t_asn1Tag*)tag)->tagClass == kASN1TagClassContextSpecific) {631tag += asn1Len((char*)tag+1).sizeBytes +1;632}633634char *magic = 0;635size_t l;636getSequenceName((char*)tag, &magic, &l);637638putStr(magic, l);printf(": ---------\n");639640if (!im4pOnly && strncmp("IM4R", magic, l) == 0) printIM4R(tag);641if (!im4pOnly && strncmp("IM4M", magic, l) == 0) printIM4M(tag,printAll);642if (strncmp("IM4P", magic, l) == 0) printIM4P(tag);643putchar('\n');644}645646error:647return;648#undef reterror649}650651652void printIM4R(char *buf){653#define reterror(a ...){error(a);goto error;}654655char *magic;656size_t l;657getSequenceName(buf, &magic, &l);658if (strncmp("IM4R", magic, l)) reterror("unexpected \"%.*s\", expected \"IM4R\"\n",(int)l,magic);659660int elems = asn1ElementsInObject(buf);661if (elems<2) reterror("expecting at least 2 elements\n");662663t_asn1Tag *set = (t_asn1Tag*)asn1ElementAtIndex(buf, 1);664if (set->tagNumber != kASN1TagSET) reterror("expecting SET type\n");665666set += asn1Len((char*)set+1).sizeBytes+1;667668if (set->tagClass != kASN1TagClassPrivate) reterror("expecting PRIVATE type\n");669670printPrivtag(asn1GetPrivateTagnum(set++,0));671printf("\n");672673set += asn1Len((char*)set).sizeBytes+1;674elems = asn1ElementsInObject((char*)set);675if (elems<2) reterror("expecting at least 2 elements\n");676677printI5AString((t_asn1Tag*)asn1ElementAtIndex((char*)set, 0));678printf(": ");679printHexString((t_asn1Tag*)asn1ElementAtIndex((char*)set, 1));680putchar('\n');681682error:683return;684#undef reterror685}686687char *getIM4PFromIMG4(char *buf){688char *magic;689size_t l;690getSequenceName(buf, &magic, &l);691if (strncmp("IMG4", magic, l)) return error("unexpected \"%.*s\", expected \"IMG4\"\n",(int)l,magic),NULL;692if (asn1ElementsInObject(buf)<2) return error("not enough elements in SEQUENCE"),NULL;693char *ret = (char*)asn1ElementAtIndex(buf, 1);694getSequenceName(ret, &magic, &l);695return (strncmp("IM4P", magic, 4) == 0) ? ret : (error("unexpected \"%.*s\", expected \"IM4P\"\n",(int)l,magic),NULL);696}697698char *getIM4MFromIMG4(char *buf){699char *magic;700size_t l;701getSequenceName(buf, &magic, &l);702if (strncmp("IMG4", magic, l)) return error("unexpected \"%.*s\", expected \"IMG4\"\n",(int)l,magic),NULL;703if (asn1ElementsInObject(buf)<3) return error("not enough elements in SEQUENCE"),NULL;704char *ret = (char*)asn1ElementAtIndex(buf, 2);705if (((t_asn1Tag*)ret)->tagClass != kASN1TagClassContextSpecific) return error("unexpected Tag 0x%02x, expected SET\n",*(unsigned char*)ret),NULL;706ret += asn1Len(ret+1).sizeBytes + 1;707getSequenceName(ret, &magic, &l);708return (strncmp("IM4M", magic, 4) == 0) ? ret : NULL;709}710711void printIM4M(char *buf, bool printAll){712#define reterror(a ...){error(a);goto error;}713714char *magic;715size_t l;716getSequenceName(buf, &magic, &l);717if (strncmp("IM4M", magic, l)) reterror("unexpected \"%.*s\", expected \"IM4M\"\n",(int)l,magic);718719int elems = asn1ElementsInObject(buf);720if (elems<2) reterror("expecting at least 2 elements\n");721722if (--elems>0) {723printf("Version: ");724printNumber((t_asn1Tag*)asn1ElementAtIndex(buf, 1));725putchar('\n');726}727if (--elems>0) {728t_asn1Tag *manbset = (t_asn1Tag*)asn1ElementAtIndex(buf, 2);729if (manbset->tagNumber != kASN1TagSET) reterror("expecting SET\n");730731t_asn1Tag *privtag = manbset + asn1Len((char*)manbset+1).sizeBytes+1;732size_t sb;733printPrivtag(asn1GetPrivateTagnum(privtag++,&sb));734printf("\n");735char *manbseq = (char*)privtag+sb;736manbseq+= asn1Len(manbseq).sizeBytes+1;737printMANB(manbseq, printAll);738if (!printAll) return;739}740741error:742return;743#undef reterror744}745746void asn1PrintValue(t_asn1Tag *tag){747if (tag->tagNumber == kASN1TagIA5String){748printI5AString(tag);749}else if (tag->tagNumber == kASN1TagOCTET){750printHexString(tag);751}else if (tag->tagNumber == kASN1TagINTEGER){752t_asn1ElemLen len = asn1Len((char*)tag+1);753unsigned char *num = (unsigned char*)tag+1 + len.sizeBytes;754uint64_t pnum = 0;755while (len.dataLen--) {756pnum *=0x100;757pnum += *num++;758759//ungly workaround for WIN32760if (sizeof(uint64_t) != 8) printf("%02x",num[-1]);761762}763if (sizeof(uint64_t) == 8) printf("%llu",pnum);764else printf(" (hex)");765}else if (tag->tagNumber == kASN1TagBOOLEAN){766printf("%s",(*(char*)tag+2 == 0) ? "false" : "true");767}else{768error("can't print unknown tag %02x\n",*(unsigned char*)tag);769}770}771772void asn1PrintRecKeyVal(char *buf){773774if (((t_asn1Tag*)buf)->tagNumber == kASN1TagSEQUENCE) {775int i;776if ((i = asn1ElementsInObject(buf)) != 2){777error("expecting 2 elements found %d\n",i);778return;779}780printI5AString((t_asn1Tag*)asn1ElementAtIndex(buf, 0));781printf(": ");782asn1PrintRecKeyVal(asn1ElementAtIndex(buf, 1));783printf("\n");784return;785}else if (((t_asn1Tag*)buf)->tagNumber != kASN1TagSET){786asn1PrintValue((t_asn1Tag *)buf);787return;788}789790791//must be a SET792printf("------------------------------\n");793for (int i = 0; i<asn1ElementsInObject(buf); i++) {794char *elem = (char*)asn1ElementAtIndex(buf, i);795size_t sb;796printPrivtag(asn1GetPrivateTagnum((t_asn1Tag*)elem,&sb));797printf(": ");798elem+=sb;799elem += asn1Len(elem+1).sizeBytes;800asn1PrintRecKeyVal(elem);801}802803}804805void printMANB(char *buf, bool printAll){806#define reterror(a ...){error(a);goto error;}807808char *magic;809size_t l;810getSequenceName(buf, &magic, &l);811if (strncmp("MANB", magic, l)) reterror("unexpected \"%.*s\", expected \"MANB\"\n",(int)l,magic);812813int manbElemsCnt = asn1ElementsInObject(buf);814if (manbElemsCnt<2) reterror("not enough elements in MANB\n");815char *manbSeq = (char*)asn1ElementAtIndex(buf, 1);816817for (int i=0; i<asn1ElementsInObject(manbSeq); i++) {818t_asn1Tag *manbElem = (t_asn1Tag*)asn1ElementAtIndex(manbSeq, i);819size_t privTag = 0;820if (*(char*)manbElem == kASN1TagPrivate) {821size_t sb;822printPrivtag(privTag = asn1GetPrivateTagnum(manbElem,&sb));823printf(": ");824manbElem+=sb;825}else manbElem++;826827manbElem += asn1Len((char*)manbElem).sizeBytes;828829asn1PrintRecKeyVal((char*)manbElem);830if (!printAll && strncmp((char*)&privTag, "PNAM", 4) == 0){831break;832}833}834835836error:837return;838#undef reterror839}840841842char *getSHA1ofSqeuence(char * buf){843if (((t_asn1Tag*)buf)->tagNumber != kASN1TagSEQUENCE){844error("tag not seuqnece");845return 0;846}847t_asn1ElemLen bLen = asn1Len(buf+1);848size_t buflen = 1 + bLen.dataLen + bLen.sizeBytes;849char *ret = malloc(SHA_DIGEST_LENGTH);850if (ret)851SHA1((unsigned char*)buf, (unsigned int)buflen, (unsigned char *)ret);852853return ret;854}855856*/857858/*int hasBuildidentityElementWithHash(plist_t identity, char *hash, uint64_t hashSize){*/859/*#define reterror(a ...){rt=0;error(a);goto error;}*/860/*#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*/861862/*int rt = 0;*/863/*plist_dict_iter dictIterator = NULL;*/864865/*plist_t manifest = plist_dict_get_item(identity, "Manifest");*/866/*if (!manifest)*/867/*reterror("can't find Manifest\n");*/868869/*plist_t node = NULL;*/870/*char *key = NULL;*/871/*plist_dict_new_iter(manifest, &dictIterator);*/872/*plist_dict_next_item(manifest, dictIterator, &key, &node);*/873/*do {*/874/*skipelem("BasebandFirmware")*/875/*skipelem("ftap")*/876/*skipelem("ftsp")*/877/*skipelem("rfta")*/878/*skipelem("rfts")*/879/*skipelem("SE,Bootloader")*/880/*skipelem("SE,Firmware")*/881/*skipelem("SE,MigrationOS")*/882/*skipelem("SE,OS")*/883/*skipelem("SE,UpdatePayload")*/884885/*plist_t digest = plist_dict_get_item(node, "Digest");*/886/*if (!digest || plist_get_node_type(digest) != PLIST_DATA)*/887/*reterror("can't find digest for key=%s\n",key);*/888889/*char *dgstData = NULL;*/890/*uint64_t len = 0;*/891/*plist_get_data_val(digest, &dgstData, &len);*/892/*if (!dgstData)*/893/*reterror("can't get dgstData for key=%s.\n",key);*/894895/*if (len == hashSize && memcmp(dgstData, hash, len) == 0)*/896/*rt = 1;*/897898/*free(dgstData);*/899/*skip:*/900/*plist_dict_next_item(manifest, dictIterator, &key, &node);*/901/*} while (!rt && node);*/902/*error:*/903/*free(dictIterator),dictIterator = NULL;*/904/*return rt;*/905/*#undef skipelem*/906/*#undef reterror*/907/*}*/908909/*plist_t findAnyBuildidentityForFilehash(plist_t identities, char *hash, uint64_t hashSize){*/910/*#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*/911/*#define reterror(a ...){rt=NULL;error(a);goto error;}*/912/*plist_t rt = NULL;*/913/*plist_dict_iter dictIterator = NULL;*/914915/*for (int i=0; !rt && i<plist_array_get_size(identities); i++) {*/916/*plist_t idi = plist_array_get_item(identities, i);*/917918/*plist_t manifest = plist_dict_get_item(idi, "Manifest");*/919/*if (!manifest)*/920/*reterror("can't find Manifest. i=%d\n",i);*/921922/*plist_t node = NULL;*/923/*char *key = NULL;*/924/*plist_dict_new_iter(manifest, &dictIterator);*/925/*plist_dict_next_item(manifest, dictIterator, &key, &node);*/926/*do {*/927/*skipelem("BasebandFirmware")*/928/*skipelem("ftap")*/929/*skipelem("ftsp")*/930/*skipelem("rfta")*/931/*skipelem("rfts")*/932933/*plist_t digest = plist_dict_get_item(node, "Digest");*/934/*if (!digest || plist_get_node_type(digest) != PLIST_DATA)*/935/*reterror("can't find digest for key=%s. i=%d\n",key,i);*/936937/*char *dgstData = NULL;*/938/*uint64_t len = 0;*/939/*plist_get_data_val(digest, &dgstData, &len);*/940/*if (!dgstData)*/941/*reterror("can't get dgstData for key=%s. i=%d\n",key,i);*/942943/*if (len == hashSize && memcmp(dgstData, hash, len) == 0)*/944/*rt = idi;*/945946/*free(dgstData);*/947/*skip:*/948/*plist_dict_next_item(manifest, dictIterator, &key, &node);*/949/*} while (!rt && node);*/950951/*free(dictIterator),dictIterator = NULL;*/952/*}*/953954/*error:*/955/*if (dictIterator) free(dictIterator);*/956/*return rt;*/957/*#undef reterror*/958/*#undef skipelem*/959/*}*/960961/*int doForDGSTinIM4M(const char *im4m, void *state, int (*loop_cb)(char elemNameStr[4], char *dgstData, size_t dgstDataLen, void *state)){*/962/*int err = 0;*/963/*#define reterror(code, msg ...) do {error(msg);err=code;goto error;}while(0)*/964965/*if (!sequenceHasName(im4m, "IM4M"))*/966/*reterror(-1,"can't find IM4M tag\n");*/967968/*char *im4mset = (char *)asn1ElementAtIndex(im4m, 2);*/969/*if (!im4mset)*/970/*reterror(-2,"can't find im4mset\n");*/971/*char *manbSeq = getValueForTagInSet(im4mset, *(uint32_t*)"BNAM");*/972/*if (!manbSeq)*/973/*reterror(-3,"can't find manbSeq\n");*/974975/*char *manbSet = (char*)asn1ElementAtIndex(manbSeq, 1);*/976/*if (!manbSet)*/977/*reterror(-4,"can't find manbSet\n");*/978979/*for (int i=0; i<asn1ElementsInObject(manbSet); i++) {*/980/*char *curr = asn1ElementAtIndex(manbSet, i);*/981982/*size_t sb;*/983/*if (asn1GetPrivateTagnum((t_asn1Tag*)curr, &sb) == *(uint32_t*)"PNAM")*/984/*continue;*/985/*char *cSeq = (char*)curr+sb;*/986/*cSeq += asn1Len(cSeq).sizeBytes;*/987988/*char *elemName = asn1ElementAtIndex(cSeq, 0);*/989/*t_asn1ElemLen elemNameLen = asn1Len(elemName+1);*/990/*char *elemNameStr = elemName + elemNameLen.sizeBytes+1;*/991992/*char *elemSet = (char*)asn1ElementAtIndex(cSeq, 1);*/993/*if (!elemSet)*/994/*reterror(-5, "can't find elemSet. i=%d\n",i);*/995996/*char *dgstSeq = getValueForTagInSet(elemSet, *(uint32_t*)"TSGD");*/997/*if (!dgstSeq)*/998/*reterror(-6, "can't find dgstSeq. i=%d\n",i);*/99910001001/*char *dgst = asn1ElementAtIndex(dgstSeq, 1);*/1002/*if (!dgst || asn1Tag(dgst)->tagNumber != kASN1TagOCTET)*/1003/*reterror(-7, "can't find DGST. i=%d\n",i);*/10041005/*t_asn1ElemLen lenDGST = asn1Len((char*)dgst+1);*/1006/*char *dgstData = (char*)dgst+lenDGST.sizeBytes+1;*/100710081009/*if ((err = loop_cb(elemNameStr, dgstData, lenDGST.dataLen, state))){*/1010/*if (err > 0){ //restart loop if err > 0*/1011/*i = -1;*/1012/*err = 0;*/1013/*continue;*/1014/*}*/1015/*break;*/1016/*}*/1017/*}*/10181019/*error:*/1020/*return err;*/1021/*#undef reterror*/1022/*}*/102310241025/*int im4m_buildidentity_check_cb(char elemNameStr[4], char *dgstData, size_t dgstDataLen, struct {plist_t rt; plist_t identities;} *state){*/1026/*#define skipelem(e) if (strncmp(e, elemNameStr,4) == 0) return 0*/1027/*skipelem("ftsp");*/1028/*skipelem("ftap");*/1029/*skipelem("rfta");*/1030/*skipelem("rfts");*/10311032/*if (state->rt){*/1033/*if (!hasBuildidentityElementWithHash(state->rt, dgstData, dgstDataLen)){*/1034/*//remove identity we are not looking for and start comparing all hashes again*/1035/*plist_array_remove_item(state->identities, plist_array_get_item_index(state->rt));*/1036/*state->rt = NULL;*/1037/*return 1; //trigger loop restart*/1038/*}*/1039/*}else{*/1040/*if (!(state->rt = findAnyBuildidentityForFilehash(state->identities, dgstData, dgstDataLen)))*/1041/*return (error("can't find any identity which matches all hashes inside IM4M\n"),-1);*/10421043/*}*/10441045/*#undef skipelem*/1046/*return 0;*/1047/*}*/10481049/*plist_t getBuildIdentityForIM4M(const char *buf, const plist_t buildmanifest){*/1050/*#define reterror(a ...){state.rt=NULL;error(a);goto error;}*/1051/*#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*/10521053/*plist_t manifest = plist_copy(buildmanifest);*/10541055/*struct {plist_t rt; plist_t identities;} state;*/1056/*state.rt = NULL;*/10571058/*state.identities = plist_dict_get_item(manifest, "BuildIdentities");*/1059/*if (!state.identities)*/1060/*reterror("can't find BuildIdentities\n");*/106110621063/*doForDGSTinIM4M(buf, (void*)&state, (int (*)(char[4], char *, size_t, void *))im4m_buildidentity_check_cb);*/10641065/*plist_t finfo = plist_dict_get_item(state.rt, "Info");*/1066/*plist_t fdevclass = plist_dict_get_item(finfo, "DeviceClass");*/1067/*plist_t fresbeh = plist_dict_get_item(finfo, "RestoreBehavior");*/10681069/*if (!finfo || !fdevclass || !fresbeh)*/1070/*reterror("found buildidentiy, but can't read information\n");*/10711072/*plist_t origIdentities = plist_dict_get_item(buildmanifest, "BuildIdentities");*/10731074/*for (int i=0; i<plist_array_get_size(origIdentities); i++) {*/1075/*plist_t curr = plist_array_get_item(origIdentities, i);*/10761077/*plist_t cinfo = plist_dict_get_item(curr, "Info");*/1078/*plist_t cdevclass = plist_dict_get_item(cinfo, "DeviceClass");*/1079/*plist_t cresbeh = plist_dict_get_item(cinfo, "RestoreBehavior");*/10801081/*if (plist_compare_node_value(cresbeh, fresbeh) && plist_compare_node_value(cdevclass, fdevclass)) {*/1082/*state.rt = curr;*/1083/*goto error;*/1084/*}*/1085/*}*/1086/*//fails if loop ended without jumping to error*/1087/*reterror("found indentity, but failed to match it with orig copy\n");*/10881089/*error:*/1090/*plist_free(manifest);*/1091/*return state.rt;*/1092/*#undef reterror*/1093/*}*/10941095/*void printGeneralBuildIdentityInformation(plist_t buildidentity){*/1096/*plist_t info = plist_dict_get_item(buildidentity, "Info");*/1097/*plist_dict_iter iter = NULL;*/1098/*plist_dict_new_iter(info, &iter);*/10991100/*plist_type t;*/1101/*plist_t node = NULL;*/1102/*char *key = NULL;*/11031104/*while (plist_dict_next_item(info, iter, &key, &node),node) {*/1105/*char *str = NULL;*/1106/*switch (t = plist_get_node_type(node)) {*/1107/*case PLIST_STRING:*/1108/*plist_get_string_val(node, &str);*/1109/*printf("%s : %s\n",key,str);*/1110/*break;*/1111/*case PLIST_BOOLEAN:*/1112/*plist_get_bool_val(node, (uint8_t*)&t);*/1113/*printf("%s : %s\n",key,((uint8_t)t) ? "YES" : "NO" );*/1114/*default:*/1115/*break;*/1116/*}*/1117/*if (str) free(str);*/1118/*}*/1119/*if (iter) free(iter);*/1120/*}*/11211122/*int verify_signature(char *data, char *sig, char *certificate, int useSHA384){*/1123/*#ifndef IMG4TOOL_NOOPENSSL*/1124/*//return 0 if signature valid, 1 if invalid, <0 if error occured*/1125/*int err = 0;*/1126/*EVP_MD_CTX *mdctx = NULL;*/1127/*#define reterror(a ...){err=1;error(a);goto error;}*/11281129/*t_asn1ElemLen dataSize = asn1Len(data+1);*/1130/*t_asn1ElemLen sigSize = asn1Len(sig+1);*/1131/*t_asn1ElemLen certSize = asn1Len(certificate+1);*/11321133/*X509 *cert = d2i_X509(NULL, (const unsigned char**)&certificate, certSize.dataLen + certSize.sizeBytes + 1);*/1134/*EVP_PKEY *certpubkey = X509_get_pubkey(cert);*/11351136/*retassure(-1, mdctx = EVP_MD_CTX_create());*/11371138/*retassure(-2, EVP_DigestVerifyInit(mdctx, NULL, (useSHA384) ? EVP_sha384() : EVP_sha1(), NULL, certpubkey) == 1);*/11391140/*retassure(-3,EVP_DigestVerifyUpdate(mdctx, data, dataSize.dataLen + dataSize.sizeBytes +1) == 1);*/11411142/*err = (EVP_DigestVerifyFinal(mdctx, (unsigned char*)sig+1 + sigSize.sizeBytes, sigSize.dataLen) != 1);*/11431144/*error:*/1145/*if(mdctx) EVP_MD_CTX_destroy(mdctx);*/1146/*return err;*/1147/*#undef reterror*/1148/*#else*/1149/*printf("[FATAL!] can't verify signature, because img4tool was compiled without openssl\n");*/1150/*return 0;*/1151/*#endif*/1152/*}*/11531154/*int find_dgst_cb(char elemNameStr[4], char *dgstData, size_t dgstDataLen, void *state){*/1155/*return memcmp(dgstData, state, dgstDataLen) == 0 ? -255 : 0; //-255 is not an error in this case, but indicates that we found our hash*/1156/*}*/11571158/*int verifyIM4MSignature(const char *buf){*/1159/*int err = 0;*/1160/*#define reterror(code,a ...){error(a);err=code;goto error;}*/11611162/*retassure(-1,asn1ElementsInObject(buf) == 5);*/1163/*char *im4m = asn1ElementAtIndex(buf, 2);*/1164/*char *sig = asn1ElementAtIndex(buf, 3);*/1165/*char *certs = asn1ElementAtIndex(buf, 4);*/11661167/*int elems = 0;*/1168/*retassure(-2, (elems = asn1ElementsInObject(certs)) >=1); //iPhone7 has 1 cert, while pre-iPhone7 have 2 certs*/11691170/*// char *bootAuthority = asn1ElementAtIndex(certs, 0); //does not exist on iPhone7*/1171/*char *tssAuthority = asn1ElementAtIndex(certs, elems-1); //is always last item*/11721173/*err = verify_signature(im4m, sig, tssAuthority, elems < 2); //use SHA384 if elems is 2 otherwise use SHA1*/11741175/*error:*/1176/*return err;*/1177/*#undef reterror*/1178/*}*/11791180/*char *getBNCHFromIM4M(const char* im4m, size_t *nonceSize){*/1181/*#define reterror(a ...){error(a);ret=NULL;goto error;}*/1182/*char *ret = NULL;*/1183/*char *mainSet = NULL;*/1184/*char *manbSet = NULL;*/1185/*char *manpSet = NULL;*/1186/*char *nonceOctet = NULL;*/1187/*char *bnch = NULL;*/1188/*size_t bnchSize = 0;*/1189/*char *manb = NULL;*/1190/*char *manp = NULL;*/1191/*char *certs = NULL;*/11921193/*if (!im4m) reterror("Got empty IM4M\n");*/11941195/*if (asn1ElementsInObject(im4m) != 5) {*/1196/*error("unexpected number of Elements (%d) in IM4M sequence\n", asn1ElementsInObject(im4m));*/1197/*goto error;*/1198/*}*/1199/*mainSet = asn1ElementAtIndex(im4m, 2);*/1200/*certs = asn1ElementAtIndex(im4m, 4);*/12011202/*manb = getValueForTagInSet((char*)mainSet, *(uint32_t*)"BNAM"); //MANB priv Tag*/1203/*if (asn1ElementsInObject(manb)< 2){*/1204/*error("unexpected number of Elements in MANB sequence\n");*/1205/*goto error;*/1206/*}*/1207/*manbSet = asn1ElementAtIndex(manb, 1);*/12081209/*manp = getValueForTagInSet((char*)manbSet, *(uint32_t*)"PNAM"); //MANP priv Tag*/1210/*if (asn1ElementsInObject(manp)< 2){*/1211/*error("unexpected number of Elements in MANP sequence\n");*/1212/*goto error;*/1213/*}*/1214/*manpSet = asn1ElementAtIndex(manp, 1);*/12151216/*bnch = getValueForTagInSet((char*)manpSet, *(uint32_t*)"HCNB"); //BNCH priv Tag*/1217/*if (asn1ElementsInObject(bnch)< 2){*/1218/*error("unexpected number of Elements in BNCH sequence\n");*/1219/*goto error;*/1220/*}*/1221/*nonceOctet = (char*)asn1ElementAtIndex(bnch, 1);*/1222/*nonceOctet++;*/12231224/*ret = nonceOctet + asn1Len(nonceOctet).sizeBytes;*/1225/*bnchSize = asn1Len(nonceOctet).dataLen;*/1226/*// iPhone 7 and above use 32 byte nonce*/1227/*if (bnchSize != (asn1ElementsInObject(certs) == 1 ? 32 : 20)) {*/1228/*reterror("BNCH size incorrect\n");*/1229/*}*/1230/*if (nonceSize) *nonceSize = bnchSize;*/12311232/*error:*/1233/*return ret;*/1234/*#undef reterror*/1235/*}*/12361237/*int verifyIMG4(char *buf, plist_t buildmanifest){*/1238/*//return 0 on valid file, positive value on invalid file, negative value when errors occured*/1239/*int err = 0;*/1240/*#define reterror(code,a ...){error(a);err=code;goto error;}*/1241/*char *im4pSHA = NULL;*/1242/*if (sequenceHasName(buf, "IMG4")){*/1243/*//verify IMG4*/1244/*char *im4p = getIM4PFromIMG4(buf);*/1245/*im4pSHA = getSHA1ofSqeuence(im4p);*/12461247/*if (!im4p) goto error;*/12481249/*buf = getElementFromIMG4(buf, "IM4M");*/1250/*}*/12511252/*if (!sequenceHasName(buf, "IM4M"))*/1253/*reterror(-1,"unable to find IM4M tag");*/12541255/*if (im4pSHA){*/1256/*if (doForDGSTinIM4M(buf, im4pSHA, find_dgst_cb) == -255)*/1257/*printf("[OK] IM4P is valid for the attached IM4M\n");*/1258/*else*/1259/*reterror(1,"IM4P can't be verified by IM4M\n");*/1260/*}*/12611262/*if ((err = verifyIM4MSignature(buf))){*/1263/*reterror((err < 0) ? err : 2, "Signature verification of IM4M failed with error=%d\n",err);*/1264/*}else*/1265/*printf("[OK] IM4M signature is verified by TssAuthority\n");*/12661267/*#warning TODO verify certificate chain*/12681269/*if (buildmanifest) {*/1270/*plist_t identity = getBuildIdentityForIM4M(buf, buildmanifest);*/1271/*if (identity){*/1272/*printf("[OK] IM4M is valid for the given BuildManifest for the following restore:\n\n");*/1273/*printGeneralBuildIdentityInformation(identity);*/12741275/*}else{*/1276/*reterror(3,"IM4M is not valid for any restore within the Buildmanifest\n");*/1277/*}*/1278/*}else{*/1279/*warning("No BuildManifest specified, can't verify restore type of APTicket\n");*/1280/*}*/128112821283/*error:*/1284/*safeFree(im4pSHA);*/1285/*return err;*/1286/*#undef reterror*/1287/*}*/12881289129012911292129312941295