#include <stdio.h> #include <fcntl.h> #include <stdlib.h> #include <ctype.h> #include <unistd.h> #define _GNU_SOURCE 1 #include <string.h> #include <sys/stat.h> // for mkdir #include <sys/mman.h> // for mmap #undef ntohl #undef ntohs #define RED "\033[0;31m" #define M0 "\e[0;30m" #define CYAN "\e[0;36m" #define M1 "\e[0;31m" #define GREY "\e[0;37m" #define M8 "\e[0;38m" #define M9 "\e[0;39m" #define GREEN "\e[0;32m" #define YELLOW "\e[0;33m" #define BLUE "\e[0;34m" #define PINK "\e[0;35m" #define NORMAL "\e[0;0m" #ifdef LINUX typedef unsigned long uint64_t; typedef unsigned short uint16_t; extern void *memmem (const void *__haystack, size_t __haystacklen, const void *__needle, size_t __needlelen); #endif /** * Apple iOS OTA/PBZX expander/unpacker/lister/searcher - by Jonathan Levin, * * http://NewOSXBook.com/ * * Free for anyone (AISE) to use, modify, etc. I won't complain :-), but I'd appreciate a mention * * Changelog: 02/08/16 - Replaced alloca with malloc () (full OTAs with too many files would have popped stack..) * * 02/17/16 - Increased tolerance for corrupt OTA - can now seek to entry in a file * * 08/31/16 - Added search in OTA. * * 02/28/18 - It's been a while - and ota now does diff! * Also tidied up and made neater * * 12/03/18 - Added -S to search for string null terminated * * The last OTA: (seriously, I'm done :-) * * 08/06/19 - Integrated @S1guza's symlink fix (Thanks, man!) * Added pbzx built-in so you don't have to use pbzx first * Added multiple file processing, compatible with shell expansion * Can now ota ...whatever... payload.0?? to iterate over all files! * Added -H to generate SHA-1 hashes for all ('*') or specific files in OTA * (SHA-1 code taken from public domain, as was lzma) * * Really, the last OTA (because this %$%#$ brought me out of retirement!) * * 10/19/21 - YAA support (for iOS 15 and maybe earlier OTAs?) * * To compile: now use attached makefile, since there are lzma dependencies * Remember to add '-DLINUX' if on Linux * * */ typedef unsigned int uint32_t; uint64_t pos = 0; void processFileInner( char *mmapped, uint32_t pos, char *name, uint32_t fileSize, uint32_t perms); #ifndef NOSHA #include "sha1.c" #endif // NOSHA #pragma pack(1) struct entry { unsigned int usually_0x210_or_0x110; unsigned short usually_0x00_00; //_00_00; unsigned int fileSize; unsigned short whatever; unsigned long long timestamp_likely; unsigned short _usually_0x20; unsigned short nameLen; unsigned short uid; unsigned short gid; unsigned short perms; char name[0]; // Followed by file contents }; #pragma pack() extern int ntohl(int); extern short ntohs(short); uint32_t swap32(uint32_t arg) { return (ntohl(arg)); } int g_list = 0; int g_verbose = 0; char *g_extract = NULL; char *g_search = NULL; char *g_hash = NULL; int g_nullTerm = 0; // Since I now diff and use open->mmap(2) on several occasions, refactored // into its own function // void *mmapFile(char *FileName, uint64_t *FileSize) { int fd = open (FileName, O_RDONLY); if (fd < 0) { perror (FileName); exit(1);} // 02/17/2016 - mmap struct stat stbuf; int rc = fstat(fd, &stbuf); char *mmapped = mmap(NULL, // void *addr, stbuf.st_size , // size_t len, PROT_READ, // int prot, MAP_PRIVATE, // int flags, fd, // int fd, 0); // off_t offset); if (mmapped == MAP_FAILED) { perror (FileName); exit(1);} if (FileSize) *FileSize = stbuf.st_size; close (fd); return (mmapped); } void hashFile (char *File, char *Name, uint32_t Size, short Perms, char *HashCriteria) { if (!HashCriteria) return; if ((HashCriteria[0] != '*') && ! strstr(Name, HashCriteria)) return ; #define HASH_SIZE 20 uint8_t Message_Digest[SHA1HashSize]; doSHA1((void*)File, Size, Message_Digest); int i = 0; printf("%s (%d bytes): ", Name, Size); for (i = 0; i < HASH_SIZE; i++) { printf("%02X", Message_Digest[i]); } printf("\n"); } void extractFile (char *File, char *Name, uint32_t Size, short Perms, char *ExtractCriteria) { // MAYBE extract file (depending if matches Criteria, or "*"). // You can modify this to include regexps, case sensitivity, what not. // presently, it's just strstr() if (!ExtractCriteria) return; if ((ExtractCriteria[0] != '*') && ! strstr(Name, ExtractCriteria)) return; uint16_t type = Perms & S_IFMT; Perms &= ~S_IFMT; if (type == S_IFDIR) { mkdir (Name, 0755); return; } if(type != S_IFREG && type != S_IFLNK) { fprintf(stderr, "Unknown file type: %o\n", type); // return; } // Ok. Extract . This is simple - just dump the file contents to its directory. // What we need to do here is parse the '/' and mkdir(2), etc. char *dirSep = strchr (Name, '/'); while (dirSep) { *dirSep = '\0'; mkdir(Name,0755); *dirSep = '/'; dirSep+=1; dirSep = strchr (dirSep, '/'); } if(type == S_IFLNK) { /* @s1guza's support for symlinks! */ /* http://newosxbook.com/forum/viewtopic.php?f=3&t=19513 */ char *target = strndup(File, Size); if(g_verbose) { fprintf(stderr, "Symlinking %s to %s\n", Name, target); } symlink(target, Name); fchmodat(AT_FDCWD, Name, Perms, AT_SYMLINK_NOFOLLOW); free(target); } else { // at this point we're out of '/'s // go back to the last /, if any if (g_verbose) { fprintf(stderr, "Dumping %d bytes to %s\n", Size, Name); } int fd = open (Name, O_WRONLY| O_CREAT); fchmod (fd, Perms); write (fd, File, Size); close (fd); } } // end extractFile void showPos() { fprintf(stderr, "POS is %lld\n", pos); } struct entry *getNextEnt (char *Mapping, uint64_t Size, uint64_t *Pos) { // Return entry at Mapping[Pos], // and advance Pos to point to next one int pos = 0; struct entry *ent =(struct entry *) (Mapping + *Pos ); if (*Pos > Size) return (NULL); *Pos += sizeof(struct entry); uint32_t entsize = swap32(ent->fileSize); uint32_t nameLen = ntohs(ent->nameLen); // Get Name (immediately after the entry) //char *name = malloc (nameLen+1); // strncpy(name, Mapping+ *Pos , nameLen); //name[nameLen] = '\0'; //printf("NAME %p IS %s, Size: %d\n", Mapping, name, entsize); //free (name); *Pos += nameLen; *Pos += entsize; return (ent); } // getNextEnt int doDiff (char *File1, char *File2, int Exists) { // There are two ways to do diff: // look at both files as archives, find diffs, then figure out diff'ing entry, // or look at file internal entries individually, then compare each of them // I chose the latter. This also (to some extent) survives file ordering // Note I'm still mmap(2)ing BOTH files. This contributes to speed, but does // have the impact of consuming lots o'RAM. That said, this is to be run on a // Linux/MacOS, and not on an i-Device, so we should be ok. uint64_t file1Size = 0; char *file1Mapping = mmapFile(File1, &file1Size); uint64_t file2Size = 0; char *file2Mapping = mmapFile(File2, &file2Size); uint64_t file1pos = 0; uint64_t file2pos = 0; struct entry *file1ent = getNextEnt (file1Mapping, file1Size, &file1pos); struct entry *file2ent = getNextEnt (file2Mapping,file2Size, &file2pos); uint64_t lastFile1pos, lastFile2pos = 0; while (file1ent && file2ent) { lastFile1pos = file1pos; lastFile2pos = file2pos; file1ent = getNextEnt (file1Mapping, file1Size, &file1pos); file2ent = getNextEnt (file2Mapping,file2Size, &file2pos); char *ent1Name = file1ent->name; char *ent2Name = file2ent->name; // Because I'm lazy: skip last entry if (file1pos > file1Size - 1000000) break; int found = 1; char *n1 = strndup(file1ent->name, ntohs(file1ent->nameLen)); if (strncmp(ent1Name, ent2Name, ntohs(file1ent->nameLen))) { // Stupid names aren't NULL terminated (AAPL don't read my comments, // apparently), so we have to copy both names in: // But that's the least of our problems: We don't know if n1 has been removed // from n2, or n2 is a new addition: uint64_t seekpos = file2pos; // seek n1 in file2: found = 0; int i = 0; struct entry *seek2ent; while (1) { seek2ent = getNextEnt (file2Mapping,file2Size, &seekpos); if (!seek2ent) { break; } // {printf("EOF\n");break;} if (memcmp(seek2ent->name,file1ent->name, ntohs(seek2ent->nameLen)) == 0) { found++; break; } else { /* i++; if (i < 200) { char *n2 = strndup(seek2ent->name, ntohs(seek2ent->nameLen)); printf("check: %s(%d) != %s(%d) -- %d\n",n2, ntohs(seek2ent->nameLen),n1, strlen(n1), memcmp(seek2ent->name,file1ent->name, ntohs(seek2ent->nameLen) )); free(n2); } */ } } // end while if (!found) { printf("%s: In file1 but not file2\n", n1); // rewind file2pos so we hit the entry again.. file2pos = lastFile2pos; } else { // Found it - align (all the rest to this point were not in file1) file2pos = seekpos; } } // name mismatch if (found) { // Identical entries - check for diffs unless we're only doing existence checks // if the sizes diff, obviously: if (!Exists) { if (file1pos - lastFile1pos != file2pos - lastFile2pos) { fprintf(stdout,"%s (different sizes)\n", n1); } else // if sizes are identical, maybe - but ignore timestamp! if (memcmp (((unsigned char *)file1ent) + sizeof(struct entry), ((unsigned char *)file2ent) + sizeof(struct entry), file1pos - lastFile1pos - sizeof(struct entry))) { fprintf(stdout,"%s\n", n1); } } free (n1); } } // end file1pos return 0; } void processFile(char *fileName); int main(int argc ,char **argv) { char *filename ="p"; int i = 0; if (argc < 2) { fprintf (stderr,"Usage: %s [-v] [-l] [...] _filename[s]_ \nWhere: -l: list files in update payload\n" "Where: [...] is one of:\n" " -e _file: extract file from update payload (use \"*\" for all files)\n" " -s _string _file: Look for occurences of _string_ in file\n" " -S _string _file: Look for occurences of _string_, NULL terminated in file\n" " -H [_file]: get hash digest of specific file (use \"*\" for all files)\n" " [-n] -d _file1 _file2: Point out differences between OTA _file1 and _file2\n" " -n to only diff names\n", argv[0]); exit(10); } int exists = 0; for (i = 1; (i < argc -1) && (argv[i][0] == '-'); i++) { // This is super quick/dirty. You might want to rewrite with getopt, etc.. if (strcmp(argv[i], "-n") == 0) { exists++; } else if (strcmp (argv[i] , "-d") == 0) { // make sure we have argv[i+1] and argv[i+2]... if (i != argc - 3) { fprintf(stderr,"-d needs exactly two arguments - two OTA files to compare\n"); exit(6); } // that the files exist... if (access (argv[i+1], F_OK)) { fprintf(stderr,"%s: not a file\n", argv[i+1]); exit(11); } if (access (argv[i+2], F_OK)) { fprintf(stderr,"%s: not a file\n", argv[i+2]); exit(12); } // then do diff return ( doDiff (argv[i+1],argv[i+2], exists)); } else if (strcmp (argv[i], "-l") == 0) { g_list++;} else if (strcmp (argv[i] , "-v") == 0) { g_verbose++;} #ifndef NOSHA else if (strcmp(argv[i], "-H") == 0) { if (i == argc -1) { fprintf(stderr, "-H: Option requires an argument (what to extract)\n"); exit(5); } g_hash = argv[i+1]; i++; } #endif else if (strcmp (argv[i], "-e") == 0) { if (i == argc -1) { fprintf(stderr, "-e: Option requires an argument (what to extract)\n"); exit(5); } g_extract = argv[i+1]; i++; } // Added 08/31/16: // and modified 12/01/2018 else if ((strcmp (argv[i], "-s") == 0) || (strcmp (argv[i], "-S") == 0)) { if (i == argc - 2) { fprintf(stderr, "%s: Option requires an argument (search string)\n", argv[i]); exit(5); } g_search = argv[i+1]; if (argv[i][1] == 'S') g_nullTerm++; i++; } else { fprintf(stderr,"Unknown option: %s\n", argv[i]); return 1; } } // Another little fix if user forgot filename, rather than try to open if (argv[argc-1][0] == '-') { fprintf(stderr,"Must supply filename\n"); exit(5); } // Loop over filenames: for (; i < argc; i++) { if (strstr(argv[i],".ecc")) continue; processFile(argv[i]); } } #define PBZX_MAGIC "pbzx" char *doPBZX (char *pbzxData, int Size, int *ExtractedSize) { #ifndef NO_PBZX #define OUT_BUFSIZE 16*1024*1024 // Largest chunk I've seen is 8MB. This is double that. char * decompressXZChunk(char *buf, int size, char *Into, int *IntoSize); uint64_t length = 0, flags = 0; char *returned = malloc(OUT_BUFSIZE); int returnedSize = OUT_BUFSIZE; int available = returnedSize; int pos = strlen(PBZX_MAGIC); flags = *((uint64_t *) pbzxData + pos); // read (fd, &flags, sizeof (uint64_t)); pos += sizeof(uint64_t); flags = __builtin_bswap64(flags); // fprintf(stderr,"Flags: 0x%llx\n", flags); int i = 0; int off = 0; int warn = 0 ; int skipChunk = 0; int rc = 0; // 03/09/2016 - Fixed for single chunks (payload.0##) files, e.g. WatchOS // and for multiple chunks. AAPL changed flags on me.. // // New OTAs use 0x800000 for more chunks, not 0x01000000. // 08/06/2019 - dang it. it's not flags - it's uncomp chunk size. uint64_t totalSize = 0; uint64_t uncompLen = flags; while (pos < Size){ i++; //printf("FLAGS: %llx\n", flags); // rc= read (fd, &flags, sizeof (uint64_t)); // check retval.. flags = *((uint64_t *) (pbzxData +pos)); pos+= sizeof(uint64_t); flags = __builtin_bswap64(flags); //printf("FLAGS: %llx\n", flags); length = *((uint64_t *) (pbzxData +pos)); //rc = read (fd, &length, sizeof (uint64_t)); pos+= sizeof(uint64_t); length = __builtin_bswap64(length); skipChunk = 0; // (i < minChunk); if (getenv("JDEBUG") != NULL) fprintf(stderr,"Chunk #%d (uncomp: %lld, comp length: %lld bytes) %s\n",i, flags,length, skipChunk? "(skipped)":""); // Let's ignore the fact I'm allocating based on user input, etc.. //char *buf = malloc (length); //int bytes = read (fd, buf, length); char *buf = pbzxData + pos; pos += length; // flags = *((uint64_t *) (pbzxData +pos)); #if 0 // 6/18/2017 - Fix for WatchOS 4.x OTA wherein the chunks are bigger than what can be read in one operation int bytes = length; int totalBytes = bytes; while (totalBytes < length) { // could be partial read bytes = read (fd, buf +totalBytes, length -totalBytes); totalBytes +=bytes; } #endif // We want the XZ header/footer if it's the payload, but prepare_payload doesn't have that, // so just warn. if (memcmp(buf, "\xfd""7zXZ", 6)) { warn++; fprintf (stderr, "Warning: Can't find XZ header at offset 0x%x. Instead have 0x%x(?).. This is likely not XZ data.\n", (buf - pbzxData), (* (uint32_t *) buf )); // Treat as uncompressed // UNCOMMENT THIS to handle uncomp XZ too.. memcpy(returned + (returnedSize - available), buf, length); totalSize += length; available -= length; } else // if we have the header, we had better have a footer, too { if (strncmp(buf + length - 2, "YZ", 2)) { warn++; fprintf (stderr, "Warning: Can't find XZ footer at 0x%llx (instead have %x). This is bad.\n", (length -2), *((unsigned short *) (buf + length - 2))); } // if (1 && !skipChunk) { // Uncompress chunk int chunkExpandedSize = available; char *ptrTo = returned + (returnedSize - available); decompressXZChunk(buf, length, returned + (returnedSize - available),&chunkExpandedSize); // printf("DECOMPRESSING to %p - %p\n", ptrTo , ptrTo + chunkExpandedSize); totalSize += chunkExpandedSize; available -= chunkExpandedSize; if (available < OUT_BUFSIZE) { returnedSize += 10 * OUT_BUFSIZE; available += 10 * OUT_BUFSIZE; // Can't use realloc! char *new = malloc(returnedSize); if (getenv("JDEBUG") != NULL)printf("REALLOCING from %p to %p ,%x, AVAIL: %x\n", returned, new, returnedSize, available); if (new) { memcpy(new, returned, returnedSize - available); free(returned); returned = new; } else { fprintf(stderr,"ERROR!\n"); exit(1);} } } warn = 0; // free (buf); // Not freeing anymore, @ryandesign :-) } } //printf("Total size: %d\n", totalSize); *ExtractedSize = totalSize; if (getenv("JDEBUG") != NULL) { int f = open ("/tmp/out1", O_WRONLY |O_CREAT); write (f, returned, totalSize); close(f); } return (returned); #else fprintf(stderr,"Not compiled with PBZX support!\n"); return (NULL); #endif } // pbzx static int64_t getULEB128(const uint8_t* p, int Max, int *len) { int64_t result = 0; int bit = 0; *len = 0; do { (*len)++; uint64_t slice = *p & 0x7f; if ( /* len == Max || */ bit >= 64 || slice << bit >> bit != slice) { printf("ULEB128 malformed? (Len %d, Max : %d, bit: %d)\n", *len, Max, bit); return 0; } else { result |= (slice << bit); bit += 7; } } while (*p++ & 0x80); return result; } #define YAA "YAA1" int YAAright(unsigned char * mmapped, int Pos, uint64_t extractedSize) { int i = 0; if (memcmp(mmapped + pos, YAA, strlen(YAA)) != 0) { fprintf(stderr," NOT YAA\n"); return -1; } #if 0 D UID=0 GID=0 MOD=00755 FLG=0x00000000 MTM=1633578838.288343149 PAT=. 0615e6f56|0x112fc46d MAGIC |ULEB | T Y P| 'D'| PAT '.' 00000000 59 41 41 31 36 00 54 59 50 31 44 50 41 54 50 00 |YAA16.TYP1DPATP.| | U_I_D_ __0__ __GID____ __0__| 00000010 00 55 49 44 31 00 47 49 44 31 00 4d 4f 44 32 ed |.UID1.GID1.MOD2.| 00000020 01 46 4c 47 31 00 4d 54 4d 54|56 6f 5e 61 00 00 |.FLG1.MTMTVo^a..| 00000030 00 00 6d c4 2f 11 59 41 41 31 35 00 54 59 50 31 |..m./. #endif // nasty because I originalkly passed mmapped + pos. *Sigh* // mmapped = mmapped +Pos; unsigned int yaaLen = mmapped[strlen(YAA)] ; memcpy(&yaaLen, mmapped + strlen(YAA), sizeof(short)); uint32_t pos = strlen(YAA) + 2; char* symlinkP = NULL; int next = 0; int dataLen = 0; int aftLen = 0; uint64_t attrVal = 0; char *attrValText = NULL; char *pat = NULL; char typ = 0; uint32_t mod = 0; int p = (!g_extract) && (!g_search); while (pos < yaaLen) { uint32_t attr = 0; //printf("READING ATTR FROM POS %d\n", pos); memcpy (&attr, mmapped + pos, 4); int len = (attr & 0xff000000) >> 24; attr &= attr & 0x00ffffff; if (strcmp(&attr, "AFT") == 0) { //printf("LEN: %d", len); len -='0'; memcpy(&aftLen, mmapped + pos + 4, len); // printf("AFTLEN: %d", aftLen); } else if (strcmp(&attr, "DAT") == 0) { if (len =='A') {memcpy (&dataLen, mmapped + pos + 4, sizeof(short)); len = 2;} if (len =='B') {memcpy (&dataLen, mmapped + pos + 4, sizeof(uint32_t)); len = 4;} } else if (strcmp(&attr, "MTM") == 0) { if (len == 'T') len =12; if (len == 'S') len = 8; } else if (strcmp(&attr,"LNK") == 0) { next = 3; int l = 0; len = 0; memcpy (&len, mmapped + pos + 4, sizeof(short)); symlinkP = alloca(len+1); memcpy(symlinkP, mmapped+ pos +4 + sizeof(short), len); symlinkP[len] = '\0'; len+=2; } else if (strcmp(&attr,"PAT") == 0) { // Null terminated attributes next = 3; int l = 0; len = 0; memcpy (&len, mmapped + pos + 4, sizeof(short)); pat = alloca(len+1); memcpy(pat, mmapped+ pos +4 + sizeof(short), len); pat[len] = '\0'; len +=2; } else { if ((len >= '0') && (len <= '9')) { len -= '0'; // :-) } else { fprintf(stderr,"Wrong length for attr %s ('%c' = 0x%x) at pos 0x%x\n", &attr, len, len, pos); } if (len > 8) { fprintf(stderr,"wrong length at pos %d\n", pos); return -3;} // Still here, so we're good: attrVal = 0; memcpy (&attrVal, mmapped + pos + 4, len); if (strcmp(&attr, "TYP") == 0) { if (p) fprintf(stdout, "TYP: %c\t", attrVal); typ = mmapped [pos + 4]; } else if (strcmp(&attr, "MOD") == 0) { if(p) fprintf(stdout, "MOD: 0%o ", attrVal); mod = attrVal; } // else if (strcmp(&attr, "UID") == 0) { } // else if (strcmp(&attr, "GID") == 0) { } // else if (strcmp(&attr, "FLG") == 0) { } else if (p) fprintf(stdout, "%s: %-3d ", (char *)&attr, attrVal); } pos+=len + 4; } if (p) { if (pat) fprintf(stdout,"PAT: %s (%lld bytes)",pat, dataLen); fprintf(stdout,"\n"); } if (pos > yaaLen) { fprintf(stderr,"POS 0x%x > YAALEN 0x%x\n", pos, yaaLen); exit(1);} //printf("Got to right pos (0x%x), data Len : %d aft: %d\n", pos,dataLen, aftLen); if (g_extract && symlinkP) { // Handling symlinks, @S1guza :-) symlink(symlinkP,pat); } else processFileInner (mmapped, pos, pat, dataLen, (typ == 'D' ? S_IFDIR: S_IFREG) | mod); return yaaLen + dataLen ; // + aftLen;; } void processFileInner( char *mmapped, uint32_t pos, char *name, uint32_t fileSize, uint32_t perms) { if (g_extract) { extractFile(mmapped +pos, name, fileSize, perms, g_extract);} // Added 08/05/19 - Hash if (g_hash) { hashFile (mmapped +pos, name, fileSize, perms, g_hash); } if (g_search){ char *found = memmem (mmapped+pos, fileSize, g_search, strlen(g_search) + (g_nullTerm ? 1 : 0)); while (found != NULL) { int relOffset = found - mmapped - pos; fprintf(stdout, "Found in Entry: %s, relative offset: 0x%x (Absolute: %lx)", name, relOffset, found - mmapped); // 12/01/18 if (g_verbose) { fputc(':', stdout); fputc(' ', stdout); char *begin = found; int i = 0 ; #define BACK_LIMIT -20 #define FRONT_LIMIT 20 while(begin[i] && i > BACK_LIMIT) { i--;} for (;begin +i < found; i++) { if (isprint(begin[i])) putc (begin[i], stdout); else putc ('.', stdout); } printf("%s%s%s",RED, g_search, NORMAL); for (i+= strlen(g_search); begin[i] &&( i < FRONT_LIMIT); i++) { if (isprint(begin[i])) putc (begin[i], stdout); else putc ('.', stdout); } } fprintf(stdout,"\n"); // keep looking.. found = memmem (found + 1, fileSize - relOffset , g_search, strlen(g_search) +( g_nullTerm ? 1: 0)); } // end while } // end g_search } // end processFile void processFile(char *FileName) { int color = (getenv("JCOLOR")!= NULL); fprintf(stderr, "%sProcessing %s%s\n", color ? RED: "", FileName, color ? NORMAL :""); //unsigned char buf[4096]; uint64_t fileSize; uint64_t mappedSize; char *actualMmapped = mmapFile(FileName, &mappedSize); fileSize = mappedSize; if (actualMmapped == MAP_FAILED) { perror (FileName); return ;} char *mmapped = actualMmapped; char *extracted = NULL; // File could be a PBZX :-) if (memcmp(mmapped, PBZX_MAGIC, strlen(PBZX_MAGIC)) ==0) { // DO PBZX first! int extractedSize = 0; extracted = doPBZX (mmapped, mappedSize, &extractedSize); mmapped = extracted; fileSize = extractedSize; printf("EXTRACTED: %p, size: 0x%llx\n",mmapped, fileSize); int o = open ("/tmp/out", O_WRONLY|O_TRUNC| O_CREAT); fchmod (o, 0644); write (o, mmapped, extractedSize); close(o); } if (memcmp(mmapped, YAA, strlen(YAA)) == 0) { int pos = 0; while (pos < fileSize) { if (! g_extract && !g_search) printf("POS 0x%04x: ", pos); int rc = YAAright(mmapped, pos, fileSize); if (rc > 0) pos += rc; else { fprintf(stderr,"Position 0x%x: Not YAA or corrupt - skipping\n", pos); // Seek to next YAA char *next = memmem(mmapped+pos, fileSize -pos, YAA, strlen(YAA)); if (next) { pos = next -mmapped; } } } return; } int i = 0; struct entry *ent = alloca (sizeof(struct entry)); pos = 0; while(pos + 3*sizeof(struct entry) < fileSize) { ent = (struct entry *) (mmapped + pos ); pos += sizeof(struct entry); if ((ent->usually_0x210_or_0x110 != 0x210 && ent->usually_0x210_or_0x110 != 0x110 && ent->usually_0x210_or_0x110 != 0x310) || ent->usually_0x00_00) { fprintf (stderr,"Corrupt entry (0x%x at pos %llu@0x%llx).. skipping\n", ent->usually_0x210_or_0x110, pos, (uint64_t)(mmapped+pos)); int skipping = 1; while (skipping) { ent = (struct entry *) (mmapped + pos ) ; while (ent->usually_0x210_or_0x110 != 0x210 && ent->usually_0x210_or_0x110 != 0x110) { // #@$#$%$# POS ISN'T ALIGNED! pos ++; ent = (struct entry *) (mmapped + pos ) ; } // read rest of entry int nl = ntohs(ent->nameLen); if (ent->usually_0x00_00 || !nl) { // fprintf(stderr,"False positive.. skipping %d\n",pos); pos+=1; } else { skipping =0; pos += sizeof(struct entry); } if (pos > fileSize) return; } } uint32_t size = swap32(ent->fileSize); // fprintf(stdout," Here - ENT at pos %d: %x and 0 marker is %x namelen: %d, fileSize: %d\n", pos, ent->usually_0x210_or_0x110, ent->usually_0x00_00, ntohs(ent->nameLen), size); uint32_t nameLen = ntohs(ent->nameLen); // Get Name (immediately after the entry) // // 02/08/2016: Fixed this from alloca() - the Apple jumbo OTAs have so many files in them (THANKS GUYS!!) // that this would exceed the stack limits (could solve with ulimit -s, or also by using // a max buf size and reusing same buf, which would be a lot nicer) // Note to AAPL: Life would have been a lot nicer if the name would have been NULL terminated.. // What's another byte per every file in a huge file such as this? // char *name = (char *) (mmapped+pos); char *name = alloca (nameLen+1); strncpy(name, mmapped+pos , nameLen); name[nameLen] = '\0'; //printf("NAME IS %s\n", name); pos += ntohs(ent->nameLen); uint32_t fileSize = swap32(ent->fileSize); uint16_t perms = ntohs(ent->perms); if (g_list){ if (g_verbose) { printf ("Entry @0x%d: UID: %d GID: %d Mode: %o Size: %d (0x%x) Namelen: %d Name: ", i, ntohs(ent->uid), ntohs(ent->gid), perms, size, size, ntohs(ent->nameLen)); } printf ("%s\n", name);} // Get size (immediately after the name) if (fileSize) { // Added 08/31/16 - And I swear I should have this from the start. // So darn simple and sooooo useful! processFileInner (mmapped, pos, name, fileSize, perms); pos +=fileSize; } } // Back to loop if (extracted) { /*printf("FREEing %p\n", extracted);*/ free (extracted);} munmap(actualMmapped, mappedSize); }