int main(int argc, char **argv) { int rv; const char *what; const char *filename; const char *outname; TheImg4 *img4; unsigned type; unsigned written; unsigned char ivkey[16 + 32]; unsigned char *iv = NULL, *key = NULL; unsigned char *output = NULL; unsigned outlen = 0; int outdup = 0; DERItem item; unsigned char *data; size_t size; if (argc < 4) { fprintf(stderr, "usage: %s {-image|-extra|-keybag|-ticket} input output [ivkey]\n", argv[0]); return 1; } what = argv[1]; filename = argv[2]; outname = argv[3]; if (argc > 4) { rv = str2hex(sizeof(ivkey), ivkey, argv[4]); if (rv == sizeof(ivkey)) { iv = ivkey; key = ivkey + 16; } } data = read_file(filename, 0, &size); if (data == NULL) { fprintf(stderr, "[e] cannot read '%s'\n", filename); return -1; } img4 = parse(data, size); if (!img4) { fprintf(stderr, "[e] cannot parse '%s'\n", filename); free(data); return -1; } rv = Img4DecodeGetPayloadType(img4, &type); if (rv) { fprintf(stderr, "[e] cannot identify '%s'\n", filename); goto err; } printf("%c%c%c%c\n", FOURCC(type)); if (!strncmp(what, "-i", 2) || !strncmp(what, "-e", 2)) { int decompress; rv = Img4DecodeGetPayload(img4, &item); if (rv) { fprintf(stderr, "[e] cannot extract payload from '%s'\n", filename); goto err; } output = item.data; outlen = item.length; if (iv && key) { if (outlen & 15) { unsigned usize = (outlen + 15) & ~15; unsigned char *tmp = calloc(1, usize); if (!tmp) { fprintf(stderr, "[e] out of memory %u\n", usize); goto err; } memcpy(tmp, output, outlen); OUTSET(tmp); } rv = Img4DecodeGetPayloadKeybag(img4, &item); if (rv || item.length == 0) { fprintf(stderr, "[w] image '%s' has no keybag\n", filename); } #ifdef USE_CORECRYPTO cccbc_one_shot(ccaes_cbc_decrypt_mode(), 32, key, iv, (outlen + 15) / 16, output, output); #else AES_KEY decryptKey; AES_set_decrypt_key(key, 256, &decryptKey); AES_cbc_encrypt(output, output, (outlen + 15) & ~15, &decryptKey, iv, AES_DECRYPT); #endif } #ifdef iOS10 if (img4->payload.compression.data && img4->payload.compression.length) { DERItem tmp[2]; uint32_t deco = 0; uint64_t usize = 0; if (DERParseSequenceContent(&img4->payload.compression, 2, DERImg4PayloadItemSpecs10c, tmp, 0) || DERParseInteger(&tmp[0], &deco) || DERParseInteger64(&tmp[1], &usize)) { fprintf(stderr, "[e] cannot get decompression info\n"); goto err; } if (deco == 1 && what[1] == 'i') { size_t asize = lzfse_decode_scratch_size(); unsigned char *dec, *aux = malloc(asize); if (!aux) { fprintf(stderr, "[e] out of memory %zu\n", asize); goto err; } dec = malloc(usize + 1); if (!dec) { fprintf(stderr, "[e] out of memory %llu\n", usize + 1); free(aux); goto err; } outlen = lzfse_decode_buffer(dec, usize + 1, output, outlen, aux); free(aux); if (outlen != usize) { fprintf(stderr, "[e] decompression error\n"); free(dec); goto err; } OUTSET(dec); } } #endif decompress = (DWORD_BE(output, 0) == 'comp' && DWORD_BE(output, 4) == 'lzss'); if (decompress && what[1] == 'i') { uint32_t csize = DWORD_BE(output, 16); uint32_t usize = DWORD_BE(output, 12); uint32_t adler = DWORD_BE(output, 8); unsigned char *dec = malloc(usize); if (outlen > 0x180 + csize) { fprintf(stderr, "[i] extra 0x%x bytes after compressed chunk\n", outlen - 0x180 - csize); } if (!dec) { fprintf(stderr, "[e] out of memory %u\n", usize); goto err; } outlen = decompress_lzss(dec, output + 0x180, csize); if (adler != lzadler32(dec, outlen)) { fprintf(stderr, "[w] adler32 mismatch\n"); } OUTSET(dec); } else if (decompress) { uint32_t csize = DWORD_BE(output, 16); uint32_t usize = outlen - 0x180 - csize; if (outlen > 0x180 + csize) { unsigned char *dec = malloc(usize); if (!dec) { fprintf(stderr, "[e] out of memory %u\n", usize); goto err; } memcpy(dec, output + 0x180 + csize, usize); outlen = usize; OUTSET(dec); } else { OUTSET(NULL); } } else if (what[1] == 'e') { OUTSET(NULL); } if (!output) { fprintf(stderr, "[e] nothing to do\n"); goto err; } } if (!strncmp(what, "-k", 2)) { rv = Img4DecodeGetPayloadKeybag(img4, &item); if (rv == 0 && item.length) { output = item.data; outlen = item.length; } else { fprintf(stderr, "[e] image '%s' has no keybag\n", filename); goto err; } } if (!strncmp(what, "-t", 2)) { bool exists = false; rv = Img4DecodeManifestExists(img4, &exists); if (rv == 0 && exists) { output = img4->manifestRaw.data; outlen = img4->manifestRaw.length; } else { fprintf(stderr, "[e] image '%s' has no ticket\n", filename); goto err; } } written = write_file(outname, output, outlen); if (written != outlen) { fprintf(stderr, "[e] cannot write '%s'\n", outname); goto err; } rv = 0; out: if (outdup) { free(output); } free(img4); free(data); return rv; err: rv = -1; goto out; }
int main(int argc, char **argv) { const char *in_file = 0; // stdin const char *out_file = 0; // stdout int op = -1; // invalid op int verbosity = 0; // quiet // Parse options for (int i = 1; i < argc;) { // no args const char *a = argv[i++]; if (strcmp(a, "-h") == 0) USAGE(argc, argv); if (strcmp(a, "-v") == 0) { verbosity++; continue; } if (strcmp(a, "-encode") == 0) { op = LZFSE_ENCODE; continue; } if (strcmp(a, "-decode") == 0) { op = LZFSE_DECODE; continue; } // one arg const char **arg_var = 0; if (strcmp(a, "-i") == 0 && in_file == 0) arg_var = &in_file; else if (strcmp(a, "-o") == 0 && out_file == 0) arg_var = &out_file; if (arg_var != 0) { // Flag is recognized. Check if there is an argument. if (i == argc) USAGE_MSG(argc, argv, "Error: Missing arg after %s\n", a); *arg_var = argv[i++]; continue; } USAGE_MSG(argc, argv, "Error: invalid flag %s\n", a); } if (op < 0) USAGE_MSG(argc, argv, "Error: -encode|-decode required\n"); // Info if (verbosity > 0) { if (op == LZFSE_ENCODE) fprintf(stderr, "LZFSE encode\n"); if (op == LZFSE_DECODE) fprintf(stderr, "LZFSE decode\n"); fprintf(stderr, "Input: %s\n", in_file ? in_file : "stdin"); fprintf(stderr, "Output: %s\n", out_file ? out_file : "stdout"); } // Load input size_t in_allocated = 0; // allocated in IN size_t in_size = 0; // used in IN uint8_t *in = 0; // input buffer int in_fd = -1; // input file desc if (in_file != 0) { // If we have a file name, open it, and allocate the exact input size struct stat st; #if defined(_WIN32) in_fd = open(in_file, O_RDONLY | O_BINARY); #else in_fd = open(in_file, O_RDONLY); #endif if (in_fd < 0) { perror(in_file); exit(1); } if (fstat(in_fd, &st) != 0) { perror(in_file); exit(1); } if (st.st_size > SIZE_MAX) { fprintf(stderr, "File is too large\n"); exit(1); } in_allocated = (size_t)st.st_size; } else { // Otherwise, read from stdin, and allocate to 1 MB, grow as needed in_allocated = 1 << 20; in_fd = 0; #if defined(_WIN32) if (setmode(in_fd, O_BINARY) == -1) { perror("setmode"); exit(1); } #endif } in = (uint8_t *)malloc(in_allocated); if (in == 0) { perror("malloc"); exit(1); } while (1) { // re-alloc if needed if (in_size == in_allocated) { if (in_allocated < (100 << 20)) in_allocated <<= 1; // double it else in_allocated += (100 << 20); // or add 100 MB if already large in = lzfse_reallocf(in, in_allocated); if (in == 0) { perror("malloc"); exit(1); } } ptrdiff_t r = read(in_fd, in + in_size, in_allocated - in_size); if (r < 0) { perror("read"); exit(1); } if (r == 0) break; // end of file in_size += (size_t)r; } if (in_file != 0) { close(in_fd); in_fd = -1; } // Size info if (verbosity > 0) { fprintf(stderr, "Input size: %zu B\n", in_size); } // Encode/decode // Compute size for result buffer; we assume here that encode shrinks size, // and that decode grows by no more than 4x. These are reasonable common- // case guidelines, but are not formally guaranteed to be satisfied. size_t out_allocated = (op == LZFSE_ENCODE) ? in_size : (4 * in_size); size_t out_size = 0; size_t aux_allocated = (op == LZFSE_ENCODE) ? lzfse_encode_scratch_size() : lzfse_decode_scratch_size(); void *aux = aux_allocated ? malloc(aux_allocated) : 0; if (aux_allocated != 0 && aux == 0) { perror("malloc"); exit(1); } uint8_t *out = (uint8_t *)malloc(out_allocated); if (out == 0) { perror("malloc"); exit(1); } double c0 = get_time(); while (1) { if (op == LZFSE_ENCODE) out_size = lzfse_encode_buffer(out, out_allocated, in, in_size, aux); else out_size = lzfse_decode_buffer(out, out_allocated, in, in_size, aux); // If output buffer was too small, grow and retry. if (out_size == 0 || (op == LZFSE_DECODE && out_size == out_allocated)) { if (verbosity > 0) fprintf(stderr, "Output buffer was too small, increasing size...\n"); out_allocated <<= 1; out = (uint8_t *)lzfse_reallocf(out, out_allocated); if (out == 0) { perror("malloc"); exit(1); } continue; } break; } double c1 = get_time(); if (verbosity > 0) { fprintf(stderr, "Output size: %zu B\n", out_size); size_t raw_size = (op == LZFSE_ENCODE) ? in_size : out_size; size_t compressed_size = (op == LZFSE_ENCODE) ? out_size : in_size; fprintf(stderr, "Compression ratio: %.3f\n", (double)raw_size / (double)compressed_size); double ns_per_byte = 1.0e9 * (c1 - c0) / (double)raw_size; double mb_per_s = (double)raw_size / 1024.0 / 1024.0 / (c1 - c0); fprintf(stderr, "Speed: %.2f ns/B, %.2f MB/s\n",ns_per_byte,mb_per_s); } // Write output int out_fd = -1; if (out_file) { #if defined(_WIN32) out_fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IWRITE); #else out_fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); #endif if (out_fd < 0) { perror(out_file); exit(1); } } else { out_fd = 1; // stdout #if defined(_WIN32) if (setmode(out_fd, O_BINARY) == -1) { perror("setmode"); exit(1); } #endif } for (size_t out_pos = 0; out_pos < out_size;) { ptrdiff_t w = write(out_fd, out + out_pos, out_size - out_pos); if (w < 0) { perror("write"); exit(1); } if (w == 0) { fprintf(stderr, "Failed to write to output file\n"); exit(1); } out_pos += (size_t)w; } if (out_file != 0) { close(out_fd); out_fd = -1; } free(in); free(out); free(aux); return 0; // OK }