Exemplo n.º 1
0
    public SvrNativeInterface()
    {
        InitAndroidInterface();

        AppUtils        = new AppUtils(this);
        ApkUtils        = new ApkUtils(this);
        BatteryUtils    = new BatteryUtils(this);
        WifiUtils       = new WifiUtils(this);
        VolumeUtils     = new VolumeUtils(this);
        BrightnessUtils = new BrightnessUtils(this);
        DateTimeUtils   = new DateTimeUtils(this);
        WFDUtils        = new WFDUtils(this);
        FotaUtils       = new FotaUtils(this);
        BluetoothUtils  = new BluetoothUtils(this);
        DeviceUtils     = new DeviceUtils(this);
    }
Exemplo n.º 2
0
        /// <summary>
        /// Returns the minimum Android version (API Level) supported by the provided APK. This is based
        /// the <code>android:minSdkVersion</code>code> attributes of the APK's <code>AndroidManifest.xml</code>.
        /// </summary>
        /// <param name="cdRecords"></param>
        /// <param name="lhfSection"></param>
        /// <returns></returns>
        private static int GetMinSdkVersionFromApk(List <CentralDirectoryRecord> cdRecords, DataSource lhfSection)
        {
            var androidManifest = GetAndroidManifestFromApk(cdRecords, lhfSection);

            if (androidManifest == null)
            {
                return(DefaultMinSdkVersion);
            }

            try
            {
                return(ApkUtils.GetMinSdkVersionFromBinaryAndroidManifest(androidManifest));
            }
            catch
            {
                return(DefaultMinSdkVersion);
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Signs the input APK and outputs the resulting signed APK. The input APK is not modified.
        /// </summary>
        public void Sign()
        {
            // Step 1. Find input APK's main ZIP sections
            using (var inputApk = new DataSource(File.OpenRead(_inputFile)))
                using (var outputApk = new FileStream(_outputFile, FileMode.Create, FileAccess.ReadWrite))
                {
                    ApkUtils.ZipSections inputZipSections;
                    try
                    {
                        inputZipSections = ApkUtils.FindZipSections(inputApk);
                    }
                    catch (ZipFormatException e)
                    {
                        throw new ApkFormatException("Malformed APK: not a ZIP archive", e);
                    }

                    long       inputApkSigningBlockOffset = -1;
                    DataSource inputApkSigningBlock       = null;

                    var apkSigningBlockAndOffset = V2SchemeVerifier.FindApkSigningBlock(inputApk, inputZipSections);
                    if (apkSigningBlockAndOffset != null)
                    {
                        inputApkSigningBlock       = apkSigningBlockAndOffset.Item1;
                        inputApkSigningBlockOffset = apkSigningBlockAndOffset.Item2;
                    }

                    var inputApkLfhSection = inputApk.Slice(0,
                                                            (inputApkSigningBlockOffset != -1)
                            ? inputApkSigningBlockOffset
                            : inputZipSections.CentralDirectoryOffset);

                    // Step 2. Parse the input APK's ZIP Central Directory
                    var inputCd        = GetZipCentralDirectory(inputApk, inputZipSections);
                    var inputCdRecords = ParseZipCentralDirectory(inputCd, inputZipSections);

                    // Step 3. Obtain a signer engine instance
                    // Construct a signer engine from the provided parameters

                    // Need to extract minSdkVersion from the APK's AndroidManifest.xml
                    var minSdkVersion = GetMinSdkVersionFromApk(inputCdRecords, inputApkLfhSection);

                    var signerEngine = new DefaultApkSignerEngine(_certificate, minSdkVersion, V1SigningEnabled, V2SigningEnabled, DigestAlgorithm);
                    // Step 4. Provide the signer engine with the input APK's APK Signing Block (if any)
                    if (inputApkSigningBlock != null)
                    {
                        signerEngine.InputApkSigningBlock(inputApkSigningBlock);
                    }


                    // Step 5. Iterate over input APK's entries and output the Local File Header + data of those
                    // entries which need to be output. Entries are iterated in the order in which their Local
                    // File Header records are stored in the file. This is to achieve better data locality in
                    // case Central Directory entries are in the wrong order.
                    var inputCdRecordsSortedByLfhOffset = new List <CentralDirectoryRecord>(inputCdRecords);
                    inputCdRecordsSortedByLfhOffset.Sort(CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR);
                    var  lastModifiedDateForNewEntries = -1;
                    var  lastModifiedTimeForNewEntries = -1;
                    long inputOffset           = 0;
                    long outputOffset          = 0;
                    var  outputCdRecordsByName = new Dictionary <string, CentralDirectoryRecord>(inputCdRecords.Count);
                    foreach (var inputCdRecord in inputCdRecordsSortedByLfhOffset)
                    {
                        var  entryName         = inputCdRecord.Name;
                        var  entryInstructions = signerEngine.InputJarEntry(entryName);
                        bool shouldOutput;
                        switch (entryInstructions.OutputPolicy)
                        {
                        case DefaultApkSignerEngine.OutputPolicy.Output:
                            shouldOutput = true;
                            break;

                        case DefaultApkSignerEngine.OutputPolicy.OutputByEngine:
                        case DefaultApkSignerEngine.OutputPolicy.Skip:
                            shouldOutput = false;
                            break;

                        default:
                            throw new ArgumentOutOfRangeException(
                                      "Unknown output policy: " + entryInstructions.OutputPolicy);
                        }
                        var inputLocalFileHeaderStartOffset = inputCdRecord.LocalFileHeaderOffset;
                        if (inputLocalFileHeaderStartOffset > inputOffset)
                        {
                            // Unprocessed data in input starting at inputOffset and ending and the start of
                            // this record's LFH. We output this data verbatim because this signer is supposed
                            // to preserve as much of input as possible.
                            var chunkSize = inputLocalFileHeaderStartOffset - inputOffset;
                            inputApkLfhSection.Feed(inputOffset, chunkSize, outputApk);
                            outputOffset += chunkSize;
                            inputOffset   = inputLocalFileHeaderStartOffset;
                        }
                        LocalFileRecord inputLocalFileRecord;
                        try
                        {
                            inputLocalFileRecord =
                                LocalFileRecord.GetRecord(
                                    inputApkLfhSection, inputCdRecord, inputApkLfhSection.Length);
                        }
                        catch (ZipFormatException e)
                        {
                            throw new ApkFormatException("Malformed ZIP entry: " + inputCdRecord.Name, e);
                        }
                        inputOffset += inputLocalFileRecord.Size;
                        var inspectEntryRequest =
                            entryInstructions.InspectJarEntryRequest;
                        if (inspectEntryRequest != null)
                        {
                            FulfillInspectInputJarEntryRequest(
                                inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest);
                        }
                        if (shouldOutput)
                        {
                            // Find the max value of last modified, to be used for new entries added by the
                            // signer.
                            var lastModifiedDate = inputCdRecord.LastModificationDate;
                            var lastModifiedTime = inputCdRecord.LastModificationTime;
                            if ((lastModifiedDateForNewEntries == -1) ||
                                (lastModifiedDate > lastModifiedDateForNewEntries) ||
                                ((lastModifiedDate == lastModifiedDateForNewEntries) &&
                                 (lastModifiedTime > lastModifiedTimeForNewEntries)))
                            {
                                lastModifiedDateForNewEntries = lastModifiedDate;
                                lastModifiedTimeForNewEntries = lastModifiedTime;
                            }
                            inspectEntryRequest = signerEngine.OutputJarEntry(entryName);
                            if (inspectEntryRequest != null)
                            {
                                FulfillInspectInputJarEntryRequest(
                                    inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest);
                            }
                            // Output entry's Local File Header + data
                            var outputLocalFileHeaderOffset = outputOffset;
                            var outputLocalFileRecordSize   =
                                OutputInputJarEntryLfhRecordPreservingDataAlignment(
                                    inputApkLfhSection,
                                    inputLocalFileRecord,
                                    outputApk,
                                    outputLocalFileHeaderOffset);
                            outputOffset += outputLocalFileRecordSize;
                            // Enqueue entry's Central Directory record for output
                            CentralDirectoryRecord outputCdRecord;
                            if (outputLocalFileHeaderOffset == inputLocalFileRecord.StartOffsetInArchive)
                            {
                                outputCdRecord = inputCdRecord;
                            }
                            else
                            {
                                outputCdRecord =
                                    inputCdRecord.CreateWithModifiedLocalFileHeaderOffset(
                                        outputLocalFileHeaderOffset);
                            }
                            outputCdRecordsByName.Add(entryName, outputCdRecord);
                        }
                    }
                    var inputLfhSectionSize = inputApkLfhSection.Length;
                    if (inputOffset < inputLfhSectionSize)
                    {
                        // Unprocessed data in input starting at inputOffset and ending and the end of the input
                        // APK's LFH section. We output this data verbatim because this signer is supposed
                        // to preserve as much of input as possible.
                        var chunkSize = inputLfhSectionSize - inputOffset;
                        inputApkLfhSection.Feed(inputOffset, chunkSize, outputApk);
                        outputOffset += chunkSize;
                        inputOffset   = inputLfhSectionSize;
                    }

                    // Step 6. Sort output APK's Central Directory records in the order in which they should
                    // appear in the output
                    var outputCdRecords = new List <CentralDirectoryRecord>(inputCdRecords.Count + 10);
                    foreach (var inputCdRecord in inputCdRecords)
                    {
                        var entryName = inputCdRecord.Name;
                        if (outputCdRecordsByName.TryGetValue(entryName, out var outputCdRecord))
                        {
                            outputCdRecords.Add(outputCdRecord);
                        }
                    }

                    // Step 7. Generate and output JAR signatures, if necessary. This may output more Local File
                    // Header + data entries and add to the list of output Central Directory records.
                    var outputJarSignatureRequest = signerEngine.OutputJarEntries();
                    if (outputJarSignatureRequest != null)
                    {
                        if (lastModifiedDateForNewEntries == -1)
                        {
                            lastModifiedDateForNewEntries = 0x3a21; // Jan 1 2009 (DOS)
                            lastModifiedTimeForNewEntries = 0;
                        }
                        foreach (var entry in outputJarSignatureRequest.AdditionalJarEntries)
                        {
                            var entryName        = entry.Name;
                            var uncompressedData = entry.Data;

                            var deflateResult         = ZipUtils.Deflate(uncompressedData);
                            var compressedData        = deflateResult.Item1;
                            var uncompressedDataCrc32 = deflateResult.Item2;

                            var inspectEntryRequest = signerEngine.OutputJarEntry(entryName);
                            if (inspectEntryRequest != null)
                            {
                                inspectEntryRequest.DataSink.Write(uncompressedData, 0, uncompressedData.Length);
                                inspectEntryRequest.Done();
                            }
                            var localFileHeaderOffset = outputOffset;
                            outputOffset +=
                                LocalFileRecord.OutputRecordWithDeflateCompressedData(
                                    entryName,
                                    lastModifiedTimeForNewEntries,
                                    lastModifiedDateForNewEntries,
                                    compressedData,
                                    uncompressedDataCrc32,
                                    uncompressedData.Length,
                                    outputApk);
                            outputCdRecords.Add(
                                CentralDirectoryRecord.CreateWithDeflateCompressedData(
                                    entryName,
                                    lastModifiedTimeForNewEntries,
                                    lastModifiedDateForNewEntries,
                                    uncompressedDataCrc32,
                                    compressedData.Length,
                                    uncompressedData.Length,
                                    localFileHeaderOffset));
                        }
                        outputJarSignatureRequest.Done();
                    }

                    // Step 8. Construct output ZIP Central Directory in an in-memory buffer
                    long outputCentralDirSizeBytes = 0;
                    foreach (var record in outputCdRecords)
                    {
                        outputCentralDirSizeBytes += record.Size;
                    }

                    var outputCentralDir = new MemoryStream((int)outputCentralDirSizeBytes);
                    foreach (var record in outputCdRecords)
                    {
                        record.CopyTo(outputCentralDir);
                    }
                    var outputCentralDirStartOffset = outputOffset;
                    var outputCentralDirRecordCount = outputCdRecords.Count;

                    // Step 9. Construct output ZIP End of Central Directory record in an in-memory buffer
                    var outputEocdBytes = EocdRecord.CreateWithModifiedCentralDirectoryInfo(
                        inputZipSections.EndOfCentralDirectory,
                        outputCentralDirRecordCount,
                        outputCentralDir.Length,
                        outputCentralDirStartOffset);
                    var outputEocd = new MemoryStream(outputEocdBytes, true);

                    // Step 10. Generate and output APK Signature Scheme v2 signatures, if necessary. This may
                    // insert an APK Signing Block just before the output's ZIP Central Directory
                    var outputApkSigingBlockRequest =
                        signerEngine.OutputZipSections(
                            outputApk,
                            outputCentralDir,
                            outputEocd);

                    outputApk.Position = outputCentralDirStartOffset;
                    if (outputApkSigingBlockRequest != null)
                    {
                        var outputApkSigningBlock = outputApkSigingBlockRequest.ApkSigningBlock;
                        outputApk.Write(outputApkSigningBlock, 0, outputApkSigningBlock.Length);
                        ZipUtils.SetZipEocdCentralDirectoryOffset(
                            outputEocd, outputCentralDirStartOffset + outputApkSigningBlock.Length);
                        outputApkSigingBlockRequest.Done();
                    }

                    // Step 11. Output ZIP Central Directory and ZIP End of Central Directory
                    outputCentralDir.Position = 0;
                    outputCentralDir.CopyTo(outputApk);

                    outputApk.Write(outputEocdBytes, 0, outputEocdBytes.Length);
                    signerEngine.OutputDone();
                }
        }