Beispiel #1
0
OutputState* loadZip(const char* ipsw) {
	return loadZip2(ipsw, FALSE);
}
Beispiel #2
0
Dictionary* parseIPSW2(const char* inputIPSW, const char* bundleRoot, char** bundlePath, OutputState** state, int useMemory) {
	Dictionary* info;
	char* infoPath;

	AbstractFile* plistFile;
	char* plist;
	FILE* inputIPSWFile;

	SHA_CTX sha1_ctx;
	char* buffer;
	int read;
	unsigned char hash[20];

	DIR* dir;
	struct dirent* ent;
	StringValue* plistSHA1String;
	unsigned int plistHash[20];
	int i;

	*bundlePath = NULL;

	inputIPSWFile = fopen(inputIPSW, "rb");
	if(!inputIPSWFile) {
		return NULL;
	}

	XLOG(0, "Hashing IPSW...\n");

	buffer = malloc(BUFFERSIZE);
	SHA1_Init(&sha1_ctx);
	while(!feof(inputIPSWFile)) {
		read = fread(buffer, 1, BUFFERSIZE, inputIPSWFile);
		SHA1_Update(&sha1_ctx, buffer, read);
	}
	SHA1_Final(hash, &sha1_ctx);
	free(buffer);

	fclose(inputIPSWFile);

	XLOG(0, "Matching IPSW in %s... (%02x%02x%02x%02x...)\n", bundleRoot, (int) hash[0], (int) hash[1], (int) hash[2], (int) hash[3]);

	dir = opendir(bundleRoot);
	if(dir == NULL) {
		XLOG(1, "Bundles directory not found\n");
		return NULL;
	}

	while((ent = readdir(dir)) != NULL) {
		if(ent->d_name[0] == '.' && (ent->d_name[1] == '\0' || (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) {
			continue;
		}

		infoPath = (char*) malloc(sizeof(char) * (strlen(bundleRoot) + sizeof(PATH_SEPARATOR) + strlen(ent->d_name) + sizeof(PATH_SEPARATOR "Info.plist")));
		sprintf(infoPath, "%s" PATH_SEPARATOR "%s" PATH_SEPARATOR "Info.plist", bundleRoot, ent->d_name);
		XLOG(0, "checking: %s\n", infoPath);

		if((plistFile = createAbstractFileFromFile(fopen(infoPath, "rb"))) != NULL) {
			plist = (char*) malloc(plistFile->getLength(plistFile));
			plistFile->read(plistFile, plist, plistFile->getLength(plistFile));
			plistFile->close(plistFile);
			info = createRoot(plist);
			free(plist);

			plistSHA1String = (StringValue*)getValueByKey(info, "SHA1");
			if(plistSHA1String) {
				sscanf(plistSHA1String->value, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
					&plistHash[0], &plistHash[1], &plistHash[2], &plistHash[3], &plistHash[4],
					&plistHash[5], &plistHash[6], &plistHash[7], &plistHash[8], &plistHash[9],
					&plistHash[10], &plistHash[11], &plistHash[12], &plistHash[13], &plistHash[14],
					&plistHash[15], &plistHash[16], &plistHash[17], &plistHash[18], &plistHash[19]);

				for(i = 0; i < 20; i++) {
					if(plistHash[i] != hash[i]) {
						break;
					}
				}

				if(i == 20) {
					*bundlePath = (char*) malloc(sizeof(char) * (strlen(bundleRoot) + sizeof(PATH_SEPARATOR) + strlen(ent->d_name)));
					sprintf(*bundlePath, "%s" PATH_SEPARATOR "%s", bundleRoot, ent->d_name);

					free(infoPath);
					break;
				}
			}

			releaseDictionary(info);
		}

		free(infoPath);
	}

	closedir(dir);

	if(*bundlePath == NULL) {
		return NULL;
	}

	*state = loadZip2(inputIPSW, useMemory);

	return info;
}
Beispiel #3
0
int main(int argc, char* argv[]) {
	init_libxpwn(&argc, argv);

	OutputState *outputState;
	size_t fileLength;

	AbstractFile *signFile;
	Dictionary *signDict = NULL;
	DataValue *ticket;
	Dictionary *dict;

	AbstractFile *manifestFile;
	Dictionary* manifest;
	struct component_t *array;

	char *plist;
	int i, j, n;
	int rv, error = 0;

	X509 *x0, *y1, *y2;
	uint64_t savecid = 0;
	const unsigned char *p;

	int verbose = FALSE;
	int fromzip = FALSE;

	if (argc < 3) {
		XLOG(0, "usage %s file.shsh BuildManifest.plist [-v] [-z]\n", argv[0]);
		return 0;
	}

	for (i = 3; i < argc; i++) {
		if (!strcmp(argv[i], "-v")) {
			verbose = TRUE;
			continue;
		}

		if (!strcmp(argv[i], "-z")) {
			fromzip = TRUE;
			continue;
		}
	}

	// XXX handle gzip/bplist files
	signFile = createAbstractFileFromFile(fopen(argv[1], "rb"));
	if (!signFile) {
		XLOG(0, "FATAL: cannot open %s\n", argv[1]);
		exit(1);
	}

	fileLength = signFile->getLength(signFile);
	plist = malloc(fileLength);
	signFile->read(signFile, plist, fileLength);
	signFile->close(signFile);
	signDict = createRoot(plist);
	free(plist);

	if (!signDict) {
		XLOG(0, "FATAL: cannot parse %s\n", argv[1]);
		exit(1);
	}

	MaxLoadZipSize = 128 * 1024;
	if (fromzip) {
		outputState = loadZip2(argv[2], TRUE); // XXX ASSERT
		manifestFile = getFileFromOutputState(&outputState, "BuildManifest.plist");
	} else {
		outputState = NULL;
		manifestFile = createAbstractFileFromFile(fopen(argv[2], "rb"));
	}
	if (!manifestFile) {
		XLOG(0, "FATAL: cannot open %s\n", argv[2]);
		exit(1);
	}

	fileLength = manifestFile->getLength(manifestFile);
	plist = malloc(fileLength);
	manifestFile->read(manifestFile, plist, fileLength);
	manifestFile->close(manifestFile);
	manifest = createRoot(plist);
	free(plist);

	if (!manifest) {
		XLOG(0, "FATAL: cannot parse %s\n", argv[2]);
		exit(1);
	}

	releaseOutput(&outputState);

	array = parseManifest(manifest, &n);
	if (!array) {
		XLOG(0, "FATAL: cannot parse manifest\n");
		exit(1);
	}

	OPENSSL_add_all_algorithms_noconf();
	p = cerb;
	x0 = d2i_X509(NULL, &p, cerb_len);
	if (!x0) {
		XLOG(0, "FATAL: cannot load root CA\n");
		exit(1);
	}

	ticket = (DataValue *)getValueByKey(signDict, "APTicket");
	if (ticket) {
		p = ticket->value;
		rv = asn1_parse2(&p, ticket->len, 0, 0);
		if (!rv || !apcert.value || !rsasig.value || !theset.value) {
			XLOG(0, "FATAL: cannot parse ticket\n");
			exit(1);
		}
		if (clen > 0 && contarray->len == 8) {
			savecid = getECID(contarray->value);
		}
		if (!savecid) {
			printf("ERROR: bad, bad ECID\n");
			error = 1;
		}

		rv = extract2Certs(apcert.value, apcert.len, &y1, &y2);
		if (rv == 0) {
			rv = cryptoMagic(x0, y1, y2, theset.value, theset.len, (unsigned char *)rsasig.value, rsasig.len, NULL);
			X509_free(y1);
			X509_free(y2);
		}
		if (rv) {
			printf("ERROR: APTicket failed crypto\n");
			error = 1;
		}
	} else {
		VERBOSE("WARNING: cannot find ticket in %s\n", argv[1]);
	}

	dict = (Dictionary *)signDict->values;
	while (dict) {
		DataValue *blob = NULL;
		DataValue *partialDigest = NULL;
		if (dict->dValue.type == DictionaryType) {
			blob = (DataValue *)getValueByKey(dict, "Blob");
			partialDigest = (DataValue *)getValueByKey(dict, "PartialDigest");
		}
		if (blob && partialDigest) {
			const char *diag = checkBlob(x0, blob, partialDigest, &savecid);
			if (diag) {
				printf("ERROR: Blob for %s is invalid (%s)\n", dict->dValue.key, diag);
				error = 1;
			} else {
				for (i = 0; i < n; i++) {
					struct component_t *centry = array + i;
					if (centry->partial &&
					    partialDigest->len == centry->partial->len &&
					    !memcmp(partialDigest->value, centry->partial->value, partialDigest->len)) {
						array[i].blob = blob;
					}
				}
			}
		}
		dict = (Dictionary *)dict->dValue.next;
	}

	if (!ticket && !savecid) {
		printf("ERROR: bad, bad ECID\n");
		error = 1;
	}

	for (i = 0; i < n; i++) {
		struct component_t *centry = array + i;
		int found = FALSE;
		for (j = 0; j < clen; j++) {
			struct tuple_t *tentry = contarray + j;
			if (tentry->len == centry->digest->len && !memcmp(tentry->value, centry->digest->value, tentry->len)) {
				found = TRUE;
			}
		}
		if (!found) {
			if (centry->blob) {
				VERBOSE("WARNING: no digest for %s (%s), but it has blob\n", centry->key, centry->path);
			} else if (!centry->required) {
				VERBOSE("WARNING: no digest for %s (%s), but it is not critical\n", centry->key, centry->path);
			} else {
				printf("ERROR: no digest for %s (%s) and no blob found\n", centry->key, centry->path);
				error = 1;
			}
		} else {
			VERBOSE("INFO: %s (%s) is signed by APTicket%s\n", centry->key, centry->path, centry->blob ? " and blob" : "");
		}
	}
	free(array);
	free(contarray);

	releaseDictionary(manifest);
	releaseDictionary(signDict);

	if (error) {
		printf("%s is BROKEN\n", argv[1]);
	} else {
		printf("%s seems usable for ECID 0x%016llX\n", argv[1], savecid);
	}

	X509_free(x0);

	EVP_cleanup();
	ERR_remove_state(0);
	CRYPTO_cleanup_all_ex_data();
	return error;
}