Example #1
0
void cleanup_and_exit() {
	unlink(tmpXALLPath);
	unlink(tmpWTFPath);
	unlink(tmpIBSSPath);
	unlink(tmpRestorePath);
	rmdir(tmpDFUPath);
	rmdir(tmpFirmwarePath);
	rmdir(tmpFilePath);
	releaseOutput(&data);

	exit(0);
}
Example #2
0
void removeFileFromOutputState(OutputState** state, const char* fileName) {
	OutputState* curFile;

	curFile = *state;
	while(curFile != NULL) {
		if(strcmp(curFile->fileName, fileName) == 0) {
			if(curFile->prev == NULL) {
				*state = curFile->next;
				(*state)->next->prev = NULL;
			} else {
				curFile->prev->next = curFile->next;
				curFile->next->prev = curFile->prev;
			}
			curFile->prev = NULL;
			curFile->next = NULL;
			releaseOutput(&curFile);
			return;
		}
		curFile = curFile->next;
	}
}
Example #3
0
File: main.c Project: 1butler1/xpwn
int main(int argc, char* argv[]) {
	printf("---------------------------PLEASE READ THIS---------------------------\n");
	printf("Please make certain that all iTunes related processes are not running\n");
	printf("at this time (use Task Manager, etc. to end them). Your iPhone/iPod\n");
	printf("must be placed into DFU mode AFTER iTunes had been turned off. This\n");
	printf(" will allow me to talk to it without iTunes getting in beforehand.\n");
	printf("USB Product ID of iPhone ought to be 0x1222\n");
	printf("---------------------------PLEASE READ THIS---------------------------\n");

	if(argc < 3) {
		printf("usage: %s <custom.ipsw> <n82ap|m68ap|n45ap>\n", argv[0]);
		printf("advanced usage: %s -f <file>\n", argv[0]);
		printf("n82ap = 3G iPhone, m68ap = First-generation iPhone, n45ap = iPod touch\n");
		return 0;
	}

	if(strcmp(argv[1], "-f") == 0) {
		download(createAbstractFileFromFile(fopen(argv[2], "rb")), 2048, 1);
		return 0;
	}

	char ibssName[100];
	sprintf(ibssName, "Firmware/dfu/iBSS.%s.RELEASE.dfu", argv[2]);

	OutputState* data = NULL;
	loadZipFile(argv[1], &data, "Firmware/dfu/WTF.s5l8900xall.RELEASE.dfu");
	loadZipFile(argv[1], &data, ibssName);

	download(getFileFromOutputState(&data, "Firmware/dfu/WTF.s5l8900xall.RELEASE.dfu"), 2048, 1);
	sleep(5);
	download(getFileFromOutputState(&data, ibssName), 2048, 1);

	releaseOutput(&data);

	return 0;
}
Example #4
0
File: main.c Project: OPK/xpwn
int main(int argc, char* argv[]) {
    init_libxpwn();

    Dictionary* info;
    Dictionary* firmwarePatches;
    Dictionary* patchDict;
    ArrayValue* patchArray;

    void* buffer;

    StringValue* actionValue;
    StringValue* pathValue;

    StringValue* fileValue;

    StringValue* patchValue;
    char* patchPath;

    char* rootFSPathInIPSW;
    io_func* rootFS;
    Volume* rootVolume;
    size_t rootSize;
    size_t preferredRootSize = 0;
    size_t minimumRootSize = 0;

    char* ramdiskFSPathInIPSW;
    unsigned int ramdiskKey[16];
    unsigned int ramdiskIV[16];
    unsigned int* pRamdiskKey = NULL;
    unsigned int* pRamdiskIV = NULL;
    io_func* ramdiskFS;
    Volume* ramdiskVolume;

    char* updateRamdiskFSPathInIPSW = NULL;

    int i;

    OutputState* outputState;

    char* bundlePath;
    char* bundleRoot = "FirmwareBundles/";

    int mergePaths;
    char* outputIPSW;

    void* imageBuffer;
    size_t imageSize;

    AbstractFile* bootloader39 = NULL;
    AbstractFile* bootloader46 = NULL;
    AbstractFile* applelogo = NULL;
    AbstractFile* recoverymode = NULL;

    char noWipe = FALSE;

    char unlockBaseband = FALSE;
    char selfDestruct = FALSE;
    char use39 = FALSE;
    char use46 = FALSE;
    char doBootNeuter = FALSE;
    char updateBB = FALSE;
    char useMemory = FALSE;

    unsigned int key[16];
    unsigned int iv[16];

    unsigned int* pKey = NULL;
    unsigned int* pIV = NULL;

    if(argc < 3) {
        XLOG(0, "usage %s <input.ipsw> <target.ipsw> [-b <bootimage.png>] [-r <recoveryimage.png>] [-s <system partition size>] [-memory] [-bbupdate] [-nowipe] [-e \"<action to exclude>\"] [[-unlock] [-use39] [-use46] [-cleanup] -3 <bootloader 3.9 file> -4 <bootloader 4.6 file>] <package1.tar> <package2.tar>...\n", argv[0]);
        return 0;
    }

    outputIPSW = argv[2];

    int* toRemove = NULL;
    int numToRemove = 0;

    for(i = 3; i < argc; i++) {
        if(argv[i][0] != '-') {
            break;
        }

        if(strcmp(argv[i], "-memory") == 0) {
            useMemory = TRUE;
            continue;
        }

        if(strcmp(argv[i], "-s") == 0) {
            int size;
            sscanf(argv[i + 1], "%d", &size);
            preferredRootSize = size;
            i++;
            continue;
        }

        if(strcmp(argv[i], "-nowipe") == 0) {
            noWipe = TRUE;
            continue;
        }

        if(strcmp(argv[i], "-bbupdate") == 0) {
            updateBB = TRUE;
            continue;
        }

        if(strcmp(argv[i], "-e") == 0) {
            numToRemove++;
            toRemove = realloc(toRemove, numToRemove * sizeof(int));
            toRemove[numToRemove - 1] = i + 1;
            i++;
            continue;
        }

        if(strcmp(argv[i], "-unlock") == 0) {
            unlockBaseband = TRUE;
            continue;
        }

        if(strcmp(argv[i], "-cleanup") == 0) {
            selfDestruct = TRUE;
            continue;
        }

        if(strcmp(argv[i], "-use39") == 0) {
            if(use46) {
                XLOG(0, "error: select only one of -use39 and -use46\n");
                exit(1);
            }
            use39 = TRUE;
            continue;
        }

        if(strcmp(argv[i], "-use46") == 0) {
            if(use39) {
                XLOG(0, "error: select only one of -use39 and -use46\n");
                exit(1);
            }
            use46 = TRUE;
            continue;
        }

        if(strcmp(argv[i], "-b") == 0) {
            applelogo = createAbstractFileFromFile(fopen(argv[i + 1], "rb"));
            if(!applelogo) {
                XLOG(0, "cannot open %s\n", argv[i + 1]);
                exit(1);
            }
            i++;
            continue;
        }

        if(strcmp(argv[i], "-r") == 0) {
            recoverymode = createAbstractFileFromFile(fopen(argv[i + 1], "rb"));
            if(!recoverymode) {
                XLOG(0, "cannot open %s\n", argv[i + 1]);
                exit(1);
            }
            i++;
            continue;
        }

        if(strcmp(argv[i], "-3") == 0) {
            bootloader39 = createAbstractFileFromFile(fopen(argv[i + 1], "rb"));
            if(!bootloader39) {
                XLOG(0, "cannot open %s\n", argv[i + 1]);
                exit(1);
            }
            i++;
            continue;
        }

        if(strcmp(argv[i], "-4") == 0) {
            bootloader46 = createAbstractFileFromFile(fopen(argv[i + 1], "rb"));
            if(!bootloader46) {
                XLOG(0, "cannot open %s\n", argv[i + 1]);
                exit(1);
            }
            i++;
            continue;
        }
    }

    mergePaths = i;

    if(use39 || use46 || unlockBaseband || selfDestruct || bootloader39 || bootloader46) {
        if(!(bootloader39) || !(bootloader46)) {
            XLOG(0, "error: you must specify both bootloader files.\n");
            exit(1);
        } else {
            doBootNeuter = TRUE;
        }
    }

    info = parseIPSW2(argv[1], bundleRoot, &bundlePath, &outputState, useMemory);
    if(info == NULL) {
        XLOG(0, "error: Could not load IPSW\n");
        exit(1);
    }

    firmwarePatches = (Dictionary*)getValueByKey(info, "FilesystemPatches");

    int j;
    for(j = 0; j < numToRemove; j++) {
        removeKey(firmwarePatches, argv[toRemove[j]]);
    }
    free(toRemove);

    firmwarePatches = (Dictionary*)getValueByKey(info, "FirmwarePatches");
    patchDict = (Dictionary*) firmwarePatches->values;
    while(patchDict != NULL) {
        fileValue = (StringValue*) getValueByKey(patchDict, "File");

        StringValue* keyValue = (StringValue*) getValueByKey(patchDict, "Key");
        StringValue* ivValue = (StringValue*) getValueByKey(patchDict, "IV");
        pKey = NULL;
        pIV = NULL;

        if(keyValue) {
            sscanf(keyValue->value, "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
                   &key[0], &key[1], &key[2], &key[3], &key[4], &key[5], &key[6], &key[7], &key[8],
                   &key[9], &key[10], &key[11], &key[12], &key[13], &key[14], &key[15]);

            pKey = key;
        }

        if(ivValue) {
            sscanf(ivValue->value, "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
                   &iv[0], &iv[1], &iv[2], &iv[3], &iv[4], &iv[5], &iv[6], &iv[7], &iv[8],
                   &iv[9], &iv[10], &iv[11], &iv[12], &iv[13], &iv[14], &iv[15]);
            pIV = iv;
        }

        if(strcmp(patchDict->dValue.key, "Restore Ramdisk") == 0) {
            ramdiskFSPathInIPSW = fileValue->value;
            if(pKey) {
                memcpy(ramdiskKey, key, sizeof(key));
                memcpy(ramdiskIV, iv, sizeof(iv));
                pRamdiskKey = ramdiskKey;
                pRamdiskIV = ramdiskIV;
            } else {
                pRamdiskKey = NULL;
                pRamdiskIV = NULL;
            }
        }

        if(strcmp(patchDict->dValue.key, "Update Ramdisk") == 0) {
            updateRamdiskFSPathInIPSW = fileValue->value;
        }

        patchValue = (StringValue*) getValueByKey(patchDict, "Patch2");
        if(patchValue) {
            if(noWipe) {
                XLOG(0, "%s: ", patchDict->dValue.key);
                fflush(stdout);
                doPatch(patchValue, fileValue, bundlePath, &outputState, pKey, pIV, useMemory);
                patchDict = (Dictionary*) patchDict->dValue.next;
                continue; /* skip over the normal Patch */
            }
        }

        patchValue = (StringValue*) getValueByKey(patchDict, "Patch");
        if(patchValue) {
            XLOG(0, "%s: ", patchDict->dValue.key);
            fflush(stdout);
            doPatch(patchValue, fileValue, bundlePath, &outputState, pKey, pIV, useMemory);
        }

        if(strcmp(patchDict->dValue.key, "AppleLogo") == 0 && applelogo) {
            XLOG(0, "replacing %s\n", fileValue->value);
            fflush(stdout);
            ASSERT((imageBuffer = replaceBootImage(getFileFromOutputState(&outputState, fileValue->value), pKey, pIV, applelogo, &imageSize)) != NULL, "failed to use new image");
            addToOutput(&outputState, fileValue->value, imageBuffer, imageSize);
        }

        if(strcmp(patchDict->dValue.key, "RecoveryMode") == 0 && recoverymode) {
            XLOG(0, "replacing %s\n", fileValue->value);
            fflush(stdout);
            ASSERT((imageBuffer = replaceBootImage(getFileFromOutputState(&outputState, fileValue->value), pKey, pIV, recoverymode, &imageSize)) != NULL, "failed to use new image");
            addToOutput(&outputState, fileValue->value, imageBuffer, imageSize);
        }

        patchDict = (Dictionary*) patchDict->dValue.next;
    }

    fileValue = (StringValue*) getValueByKey(info, "RootFilesystem");
    rootFSPathInIPSW = fileValue->value;

    size_t defaultRootSize = ((IntegerValue*) getValueByKey(info, "RootFilesystemSize"))->value;
    minimumRootSize = defaultRootSize * 1000 * 1000;
    minimumRootSize -= minimumRootSize % 512;

    if(preferredRootSize == 0) {
        preferredRootSize = defaultRootSize;
    }

    rootSize =  preferredRootSize * 1000 * 1000;
    rootSize -= rootSize % 512;

    if(useMemory) {
        buffer = malloc(rootSize);
    } else {
        buffer = NULL;
    }

    if(buffer == NULL) {
        XLOG(2, "using filesystem backed temporary storage\n");
    }

    extractDmg(
        createAbstractFileFromFileVault(getFileFromOutputState(&outputState, rootFSPathInIPSW), ((StringValue*)getValueByKey(info, "RootFilesystemKey"))->value),
        openRoot((void**)&buffer, &rootSize), -1);


    rootFS = IOFuncFromAbstractFile(openRoot((void**)&buffer, &rootSize));
    rootVolume = openVolume(rootFS);
    XLOG(0, "Growing root to minimum: %ld\n", (long) defaultRootSize);
    fflush(stdout);
    grow_hfs(rootVolume, minimumRootSize);
    if(rootSize > minimumRootSize) {
        XLOG(0, "Growing root: %ld\n", (long) preferredRootSize);
        fflush(stdout);
        grow_hfs(rootVolume, rootSize);
    }

    firmwarePatches = (Dictionary*)getValueByKey(info, "FilesystemPatches");
    patchArray = (ArrayValue*) firmwarePatches->values;
    while(patchArray != NULL) {
        for(i = 0; i < patchArray->size; i++) {
            patchDict = (Dictionary*) patchArray->values[i];
            fileValue = (StringValue*) getValueByKey(patchDict, "File");

            actionValue = (StringValue*) getValueByKey(patchDict, "Action");
            if(strcmp(actionValue->value, "ReplaceKernel") == 0) {
                pathValue = (StringValue*) getValueByKey(patchDict, "Path");
                XLOG(0, "replacing kernel... %s -> %s\n", fileValue->value, pathValue->value);
                fflush(stdout);
                add_hfs(rootVolume, getFileFromOutputState(&outputState, fileValue->value), pathValue->value);
            }
            if(strcmp(actionValue->value, "Patch") == 0) {
                patchValue = (StringValue*) getValueByKey(patchDict, "Patch");
                patchPath = (char*) malloc(sizeof(char) * (strlen(bundlePath) + strlen(patchValue->value) + 2));
                strcpy(patchPath, bundlePath);
                strcat(patchPath, "/");
                strcat(patchPath, patchValue->value);

                XLOG(0, "patching %s (%s)... ", fileValue->value, patchPath);
                doPatchInPlace(rootVolume, fileValue->value, patchPath);
                free(patchPath);
            }
        }

        patchArray = (ArrayValue*) patchArray->dValue.next;
    }

    for(; mergePaths < argc; mergePaths++) {
        XLOG(0, "merging %s\n", argv[mergePaths]);
        AbstractFile* tarFile = createAbstractFileFromFile(fopen(argv[mergePaths], "rb"));
        if(tarFile == NULL) {
            XLOG(1, "cannot find %s, make sure your slashes are in the right direction\n", argv[mergePaths]);
            releaseOutput(&outputState);
            closeRoot(buffer);
            exit(0);
        }
        hfs_untar(rootVolume, tarFile);
        tarFile->close(tarFile);
    }

    if(pRamdiskKey) {
        ramdiskFS = IOFuncFromAbstractFile(openAbstractFile2(getFileFromOutputStateForOverwrite(&outputState, ramdiskFSPathInIPSW), pRamdiskKey, pRamdiskIV));
    } else {
        XLOG(0, "unencrypted ramdisk\n");
        ramdiskFS = IOFuncFromAbstractFile(openAbstractFile(getFileFromOutputStateForOverwrite(&outputState, ramdiskFSPathInIPSW)));
    }
    ramdiskVolume = openVolume(ramdiskFS);
    XLOG(0, "growing ramdisk: %d -> %d\n", ramdiskVolume->volumeHeader->totalBlocks * ramdiskVolume->volumeHeader->blockSize, (ramdiskVolume->volumeHeader->totalBlocks + 4) * ramdiskVolume->volumeHeader->blockSize);
    grow_hfs(ramdiskVolume, (ramdiskVolume->volumeHeader->totalBlocks + 4) * ramdiskVolume->volumeHeader->blockSize);

    if(doBootNeuter) {
        firmwarePatches = (Dictionary*)getValueByKey(info, "BasebandPatches");
        if(firmwarePatches != NULL) {
            patchDict = (Dictionary*) firmwarePatches->values;
            while(patchDict != NULL) {
                pathValue = (StringValue*) getValueByKey(patchDict, "Path");

                fileValue = (StringValue*) getValueByKey(patchDict, "File");
                if(fileValue) {
                    XLOG(0, "copying %s -> %s... ", fileValue->value, pathValue->value);
                    fflush(stdout);
                    if(copyAcrossVolumes(ramdiskVolume, rootVolume, fileValue->value, pathValue->value)) {
                        patchValue = (StringValue*) getValueByKey(patchDict, "Patch");
                        if(patchValue) {
                            patchPath = malloc(sizeof(char) * (strlen(bundlePath) + strlen(patchValue->value) + 2));
                            strcpy(patchPath, bundlePath);
                            strcat(patchPath, "/");
                            strcat(patchPath, patchValue->value);
                            XLOG(0, "patching %s (%s)... ", pathValue->value, patchPath);
                            fflush(stdout);
                            doPatchInPlace(rootVolume, pathValue->value, patchPath);
                            free(patchPath);
                        }
                    }
                }

                if(strcmp(patchDict->dValue.key, "Bootloader 3.9") == 0 && bootloader39 != NULL) {
                    add_hfs(rootVolume, bootloader39, pathValue->value);
                }

                if(strcmp(patchDict->dValue.key, "Bootloader 4.6") == 0 && bootloader46 != NULL) {
                    add_hfs(rootVolume, bootloader46, pathValue->value);
                }

                patchDict = (Dictionary*) patchDict->dValue.next;
            }
        }

        fixupBootNeuterArgs(rootVolume, unlockBaseband, selfDestruct, use39, use46);
    }

    createRestoreOptions(ramdiskVolume, preferredRootSize, updateBB);
    closeVolume(ramdiskVolume);
    CLOSE(ramdiskFS);

    if(updateRamdiskFSPathInIPSW)
        removeFileFromOutputState(&outputState, updateRamdiskFSPathInIPSW);

    closeVolume(rootVolume);
    CLOSE(rootFS);

    buildDmg(openRoot((void**)&buffer, &rootSize), getFileFromOutputStateForReplace(&outputState, rootFSPathInIPSW));

    closeRoot(buffer);

    writeOutput(&outputState, outputIPSW);

    releaseDictionary(info);

    free(bundlePath);

    return 0;
}
Example #5
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;
}
status_t BnAudioPolicyService::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch (code) {
        case SET_DEVICE_CONNECTION_STATE: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_devices_t device =
                    static_cast <audio_devices_t>(data.readInt32());
            audio_policy_dev_state_t state =
                    static_cast <audio_policy_dev_state_t>(data.readInt32());
            const char *device_address = data.readCString();
            reply->writeInt32(static_cast<uint32_t> (setDeviceConnectionState(device,
                                                                              state,
                                                                              device_address)));
            return NO_ERROR;
        } break;

        case GET_DEVICE_CONNECTION_STATE: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_devices_t device =
                    static_cast<audio_devices_t> (data.readInt32());
            const char *device_address = data.readCString();
            reply->writeInt32(static_cast<uint32_t> (getDeviceConnectionState(device,
                                                                              device_address)));
            return NO_ERROR;
        } break;

        case SET_PHONE_STATE: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            reply->writeInt32(static_cast <uint32_t>(setPhoneState(
                    (audio_mode_t) data.readInt32())));
            return NO_ERROR;
        } break;

        case SET_FORCE_USE: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_policy_force_use_t usage = static_cast <audio_policy_force_use_t>(
                    data.readInt32());
            audio_policy_forced_cfg_t config =
                    static_cast <audio_policy_forced_cfg_t>(data.readInt32());
            reply->writeInt32(static_cast <uint32_t>(setForceUse(usage, config)));
            return NO_ERROR;
        } break;

        case GET_FORCE_USE: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_policy_force_use_t usage = static_cast <audio_policy_force_use_t>(
                    data.readInt32());
            reply->writeInt32(static_cast <uint32_t>(getForceUse(usage)));
            return NO_ERROR;
        } break;

        case GET_OUTPUT: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_stream_type_t stream =
                    static_cast <audio_stream_type_t>(data.readInt32());
            uint32_t samplingRate = data.readInt32();
            audio_format_t format = (audio_format_t) data.readInt32();
            audio_channel_mask_t channelMask = data.readInt32();
            audio_output_flags_t flags =
                    static_cast <audio_output_flags_t>(data.readInt32());

            audio_io_handle_t output = getOutput(stream,
                                                 samplingRate,
                                                 format,
                                                 channelMask,
                                                 flags);
            reply->writeInt32(static_cast <int>(output));
            return NO_ERROR;
        } break;

        case START_OUTPUT: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
            uint32_t stream = data.readInt32();
            int session = data.readInt32();
            reply->writeInt32(static_cast <uint32_t>(startOutput(output,
                                                                 (audio_stream_type_t)stream,
                                                                 session)));
            return NO_ERROR;
        } break;

        case STOP_OUTPUT: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
            uint32_t stream = data.readInt32();
            int session = data.readInt32();
            reply->writeInt32(static_cast <uint32_t>(stopOutput(output,
                                                                (audio_stream_type_t)stream,
                                                                session)));
            return NO_ERROR;
        } break;

        case RELEASE_OUTPUT: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
            releaseOutput(output);
            return NO_ERROR;
        } break;

        case GET_INPUT: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_source_t inputSource = (audio_source_t) data.readInt32();
            uint32_t samplingRate = data.readInt32();
            audio_format_t format = (audio_format_t) data.readInt32();
            audio_channel_mask_t channelMask = data.readInt32();
            int audioSession = data.readInt32();
            audio_io_handle_t input = getInput(inputSource,
                                               samplingRate,
                                               format,
                                               channelMask,
                                               audioSession);
            reply->writeInt32(static_cast <int>(input));
            return NO_ERROR;
        } break;

        case START_INPUT: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
            reply->writeInt32(static_cast <uint32_t>(startInput(input)));
            return NO_ERROR;
        } break;

        case STOP_INPUT: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
            reply->writeInt32(static_cast <uint32_t>(stopInput(input)));
            return NO_ERROR;
        } break;

        case RELEASE_INPUT: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
            releaseInput(input);
            return NO_ERROR;
        } break;

        case INIT_STREAM_VOLUME: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_stream_type_t stream =
                    static_cast <audio_stream_type_t>(data.readInt32());
            int indexMin = data.readInt32();
            int indexMax = data.readInt32();
            reply->writeInt32(static_cast <uint32_t>(initStreamVolume(stream, indexMin,indexMax)));
            return NO_ERROR;
        } break;

        case SET_STREAM_VOLUME: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_stream_type_t stream =
                    static_cast <audio_stream_type_t>(data.readInt32());
            int index = data.readInt32();
            audio_devices_t device = static_cast <audio_devices_t>(data.readInt32());
            reply->writeInt32(static_cast <uint32_t>(setStreamVolumeIndex(stream,
                                                                          index,
                                                                          device)));
            return NO_ERROR;
        } break;

        case GET_STREAM_VOLUME: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_stream_type_t stream =
                    static_cast <audio_stream_type_t>(data.readInt32());
            audio_devices_t device = static_cast <audio_devices_t>(data.readInt32());
            int index;
            status_t status = getStreamVolumeIndex(stream, &index, device);
            reply->writeInt32(index);
            reply->writeInt32(static_cast <uint32_t>(status));
            return NO_ERROR;
        } break;

        case GET_STRATEGY_FOR_STREAM: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_stream_type_t stream =
                    static_cast <audio_stream_type_t>(data.readInt32());
            reply->writeInt32(getStrategyForStream(stream));
            return NO_ERROR;
        } break;

        case GET_DEVICES_FOR_STREAM: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_stream_type_t stream =
                    static_cast <audio_stream_type_t>(data.readInt32());
            reply->writeInt32(static_cast <int>(getDevicesForStream(stream)));
            return NO_ERROR;
        } break;

        case GET_OUTPUT_FOR_EFFECT: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            effect_descriptor_t desc;
            data.read(&desc, sizeof(effect_descriptor_t));
            audio_io_handle_t output = getOutputForEffect(&desc);
            reply->writeInt32(static_cast <int>(output));
            return NO_ERROR;
        } break;

        case REGISTER_EFFECT: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            effect_descriptor_t desc;
            data.read(&desc, sizeof(effect_descriptor_t));
            audio_io_handle_t io = data.readInt32();
            uint32_t strategy = data.readInt32();
            int session = data.readInt32();
            int id = data.readInt32();
            reply->writeInt32(static_cast <int32_t>(registerEffect(&desc,
                                                                   io,
                                                                   strategy,
                                                                   session,
                                                                   id)));
            return NO_ERROR;
        } break;

        case UNREGISTER_EFFECT: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            int id = data.readInt32();
            reply->writeInt32(static_cast <int32_t>(unregisterEffect(id)));
            return NO_ERROR;
        } break;

        case SET_EFFECT_ENABLED: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            int id = data.readInt32();
            bool enabled = static_cast <bool>(data.readInt32());
            reply->writeInt32(static_cast <int32_t>(setEffectEnabled(id, enabled)));
            return NO_ERROR;
        } break;

        case IS_STREAM_ACTIVE: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_stream_type_t stream = (audio_stream_type_t) data.readInt32();
            uint32_t inPastMs = (uint32_t)data.readInt32();
            reply->writeInt32( isStreamActive((audio_stream_type_t) stream, inPastMs) );
            return NO_ERROR;
        } break;

        case IS_STREAM_ACTIVE_REMOTELY: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_stream_type_t stream = (audio_stream_type_t) data.readInt32();
            uint32_t inPastMs = (uint32_t)data.readInt32();
            reply->writeInt32( isStreamActiveRemotely((audio_stream_type_t) stream, inPastMs) );
            return NO_ERROR;
        } break;

        case IS_SOURCE_ACTIVE: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            audio_source_t source = (audio_source_t) data.readInt32();
            reply->writeInt32( isSourceActive(source));
            return NO_ERROR;
        }

        case QUERY_DEFAULT_PRE_PROCESSING: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            int audioSession = data.readInt32();
            uint32_t count = data.readInt32();
            uint32_t retCount = count;
            effect_descriptor_t *descriptors =
                    (effect_descriptor_t *)new char[count * sizeof(effect_descriptor_t)];
            status_t status = queryDefaultPreProcessing(audioSession, descriptors, &retCount);
            reply->writeInt32(status);
            if (status != NO_ERROR && status != NO_MEMORY) {
                retCount = 0;
            }
            reply->writeInt32(retCount);
            if (retCount) {
                if (retCount < count) {
                    count = retCount;
                }
                reply->write(descriptors, sizeof(effect_descriptor_t) * count);
            }
            delete[] descriptors;
            return status;
        }

        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}