Example #1
0
        private Result SetPk11Storage()
        {
            // Read the PK11 header from the body storage
            int pk11Offset = IsModern ? ModernStage1Size : LegacyStage1Size;

            Result rc = BodyStorage.Read(pk11Offset, SpanHelpers.AsByteSpan(ref _pk11Header));

            if (rc.IsFailure())
            {
                return(rc);
            }

            // Check if PK11 is already decrypted, creating the PK11 storage if it is
            IsDecrypted = _pk11Header.Magic == Package1Pk11Header.ExpectedMagic;

            if (IsDecrypted)
            {
                Pk11Storage = new SubStorage(BodyStorage, pk11Offset, Pk11Size);
                return(Result.Success);
            }

            var encPk11Storage = new SubStorage(BodyStorage, pk11Offset, Pk11Size);

            // See if we have an Erista package1 key that can decrypt this PK11
            if (!IsMariko && TryFindEristaKeyRevision())
            {
                IsDecrypted = true;
                IStorage decPk11Storage;

                if (IsModern)
                {
                    decPk11Storage = new AesCbcStorage(encPk11Storage, KeySet.Package1Keys[KeyRevision],
                                                       _stage1Footer.Iv, true);
                }
                else
                {
                    decPk11Storage = new Aes128CtrStorage(encPk11Storage,
                                                          KeySet.Package1Keys[KeyRevision].DataRo.ToArray(), _stage1Footer.Iv.ToArray(), true);
                }

                Pk11Storage = new CachedStorage(decPk11Storage, 0x4000, 1, true);

                return(Result.Success);
            }

            // We can't decrypt the PK11. Set Pk11Storage to the encrypted PK11 storage
            Pk11Storage = encPk11Storage;
            return(Result.Success);
        }
Example #2
0
        private IStorage OpenAesCtrExStorage(IStorage baseStorage, int index, bool decrypting)
        {
            NcaFsHeader    fsHeader = GetFsHeader(index);
            NcaFsPatchInfo info     = fsHeader.GetPatchInfo();

            long sectionOffset = Header.GetSectionStartOffset(index);
            long sectionSize   = Header.GetSectionSize(index);

            long bktrOffset = info.RelocationTreeOffset;
            long bktrSize   = sectionSize - bktrOffset;
            long dataSize   = info.RelocationTreeOffset;

            byte[] key       = GetContentKey(NcaKeyType.AesCtr);
            byte[] counter   = Aes128CtrStorage.CreateCounter(fsHeader.Counter, bktrOffset + sectionOffset);
            byte[] counterEx = Aes128CtrStorage.CreateCounter(fsHeader.Counter, sectionOffset);

            IStorage bucketTreeData;
            IStorage outputBucketTreeData;

            if (decrypting)
            {
                bucketTreeData       = new CachedStorage(new Aes128CtrStorage(baseStorage.Slice(bktrOffset, bktrSize), key, counter, true), 4, true);
                outputBucketTreeData = bucketTreeData;
            }
            else
            {
                bucketTreeData       = baseStorage.Slice(bktrOffset, bktrSize);
                outputBucketTreeData = new CachedStorage(new Aes128CtrStorage(baseStorage.Slice(bktrOffset, bktrSize), key, counter, true), 4, true);
            }

            var encryptionBucketTreeData = new SubStorage(bucketTreeData,
                                                          info.EncryptionTreeOffset - bktrOffset, sectionSize - info.EncryptionTreeOffset);

            var cachedBucketTreeData = new CachedStorage(encryptionBucketTreeData, IndirectStorage.NodeSize, 6, true);

            var treeHeader = new BucketTree.Header();

            info.EncryptionTreeHeader.CopyTo(SpanHelpers.AsByteSpan(ref treeHeader));
            long nodeStorageSize  = IndirectStorage.QueryNodeStorageSize(treeHeader.EntryCount);
            long entryStorageSize = IndirectStorage.QueryEntryStorageSize(treeHeader.EntryCount);

            var tableNodeStorage  = new SubStorage(cachedBucketTreeData, 0, nodeStorageSize);
            var tableEntryStorage = new SubStorage(cachedBucketTreeData, nodeStorageSize, entryStorageSize);

            IStorage decStorage = new Aes128CtrExStorage(baseStorage.Slice(0, dataSize), tableNodeStorage,
                                                         tableEntryStorage, treeHeader.EntryCount, key, counterEx, true);

            return(new ConcatenationStorage(new[] { decStorage, outputBucketTreeData }, true));
        }
Example #3
0
        /// <summary>
        /// Opens a decrypted <see cref="IStorage"/> of the entire package.
        /// </summary>
        /// <param name="packageStorage">If the method returns successfully, contains a decrypted
        /// <see cref="IStorage"/> of the package.</param>
        /// <returns>The <see cref="Result"/> of the operation.</returns>
        public Result OpenDecryptedPackage(out IStorage packageStorage)
        {
            var storages = new List <IStorage>(4);

            // The signature and IV are unencrypted
            int unencryptedHeaderSize = Package2Header.SignatureSize + Unsafe.SizeOf <Buffer16>();
            int encryptedHeaderSize   = Unsafe.SizeOf <Package2Header>() - unencryptedHeaderSize;

            // Get signature and IV
            storages.Add(new SubStorage(_storage, 0, unencryptedHeaderSize));

            // Open decrypted meta
            var encMetaStorage = new SubStorage(_storage, unencryptedHeaderSize, encryptedHeaderSize);

            // The counter starts counting at the beginning of the meta struct, but the first block in
            // the struct isn't encrypted. Increase the counter by one to skip that block.
            byte[] iv = _header.Meta.HeaderIv.Bytes.ToArray();
            Utilities.IncrementByteArray(iv);

            storages.Add(new CachedStorage(new Aes128CtrStorage(encMetaStorage, _key.DataRo.ToArray(), iv, true), 0x100, 1, true));

            // Open all the payloads
            for (int i = 0; i < Package2Header.PayloadCount; i++)
            {
                if (_header.Meta.PayloadSizes[i] == 0)
                {
                    continue;
                }

                Result rc = OpenPayload(out IStorage payloadStorage, i);
                if (rc.IsFailure())
                {
                    UnsafeHelpers.SkipParamInit(out packageStorage);
                    return(rc);
                }

                storages.Add(payloadStorage);
            }

            packageStorage = new ConcatenationStorage(storages, true);
            return(Result.Success);
        }
Example #4
0
        public static void ProcessPk11(Context ctx)
        {
            using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read))
            {
                var package1 = new LibHac.Boot.Package1();
                package1.Initialize(ctx.KeySet, file).ThrowIfFailure();

                ctx.Logger.LogMessage(package1.Print());

                string outDir = ctx.Options.OutDir;

                if (package1.IsDecrypted && outDir != null)
                {
                    Directory.CreateDirectory(outDir);

                    IStorage decryptedStorage = package1.OpenDecryptedPackage1Storage();

                    WriteFile(decryptedStorage, "Decrypted.bin");
                    WriteFile(package1.OpenWarmBootStorage(), "Warmboot.bin");
                    WriteFile(package1.OpenNxBootloaderStorage(), "NX_Bootloader.bin");
                    WriteFile(package1.OpenSecureMonitorStorage(), "Secure_Monitor.bin");

                    if (package1.IsMariko)
                    {
                        WriteFile(package1.OpenDecryptedWarmBootStorage(), "Warmboot_Decrypted.bin");

                        var marikoOemLoader = new SubStorage(decryptedStorage, Unsafe.SizeOf <Package1MarikoOemHeader>(),
                                                             package1.MarikoOemHeader.Size);

                        WriteFile(marikoOemLoader, "Mariko_OEM_Bootloader.bin");
                    }
                }

                void WriteFile(IStorage storage, string filename)
                {
                    string path = Path.Combine(outDir, filename);

                    ctx.Logger.LogMessage($"Writing {path}...");
                    storage.WriteAllBytes(path, ctx.Logger);
                }
            }
        }
        public Result CreateNormal(GameCardHandle handle, out IStorage storage)
        {
            storage = default;

            if (GameCard.IsGameCardHandleInvalid(handle))
            {
                return(ResultFs.InvalidGameCardHandleOnOpenNormalPartition.Log());
            }

            var baseStorage = new ReadOnlyGameCardStorage(GameCard, handle);

            Result rc = GameCard.GetCardInfo(out GameCardInfo cardInfo, handle);

            if (rc.IsFailure())
            {
                return(rc);
            }

            storage = new SubStorage(baseStorage, 0, cardInfo.SecureAreaOffset);
            return(Result.Success);
        }
Example #6
0
        /// <summary>
        /// Gets the raw input KIP file.
        /// </summary>
        /// <param name="kipData">If the operation returns successfully, an <see cref="IStorage"/>
        /// containing the KIP data.</param>
        /// <returns>The <see cref="Result"/> of the operation.</returns>
        public Result GetRawData(out IStorage kipData)
        {
            kipData = default;

            int kipFileSize = GetFileSize();

            Result rc = KipStorage.GetSize(out long inputFileSize);

            if (rc.IsFailure())
            {
                return(rc);
            }

            // Verify the input KIP file isn't truncated
            if (inputFileSize < kipFileSize)
            {
                return(ResultLibHac.InvalidKipFileSize.Log());
            }

            kipData = new SubStorage(KipStorage, 0, kipFileSize);
            return(Result.Success);
        }
Example #7
0
        public IStorage OpenRawStorageWithPatch(Nca patchNca, int index)
        {
            IStorage patchStorage = patchNca.OpenRawStorage(index);
            IStorage baseStorage  = SectionExists(index) ? OpenRawStorage(index) : new NullStorage();

            patchStorage.GetSize(out long patchSize).ThrowIfFailure();
            baseStorage.GetSize(out long baseSize).ThrowIfFailure();

            NcaFsHeader    header    = patchNca.Header.GetFsHeader(index);
            NcaFsPatchInfo patchInfo = header.GetPatchInfo();

            if (patchInfo.RelocationTreeSize == 0)
            {
                return(patchStorage);
            }

            var treeHeader = new BucketTree.Header();

            patchInfo.RelocationTreeHeader.CopyTo(SpanHelpers.AsByteSpan(ref treeHeader));
            long nodeStorageSize  = IndirectStorage.QueryNodeStorageSize(treeHeader.EntryCount);
            long entryStorageSize = IndirectStorage.QueryEntryStorageSize(treeHeader.EntryCount);

            var relocationTableStorage = new SubStorage(patchStorage, patchInfo.RelocationTreeOffset, patchInfo.RelocationTreeSize);
            var cachedTableStorage     = new CachedStorage(relocationTableStorage, IndirectStorage.NodeSize, 4, true);

            var tableNodeStorage  = new SubStorage(cachedTableStorage, 0, nodeStorageSize);
            var tableEntryStorage = new SubStorage(cachedTableStorage, nodeStorageSize, entryStorageSize);

            var storage = new IndirectStorage();

            storage.Initialize(tableNodeStorage, tableEntryStorage, treeHeader.EntryCount).ThrowIfFailure();

            storage.SetStorage(0, baseStorage, 0, baseSize);
            storage.SetStorage(1, patchStorage, 0, patchSize);

            return(storage);
        }
Example #8
0
        /// <summary>
        /// Opens a decrypted <see cref="IStorage"/> of one of the payloads in the package.
        /// </summary>
        /// <param name="payloadStorage">If the method returns successfully, contains an <see cref="IStorage"/>
        /// of the specified payload.</param>
        /// <param name="index">The index of the payload to get. Must me less than <see cref="Package2Header.PayloadCount"/></param>
        /// <returns>The <see cref="Result"/> of the operation.</returns>
        public Result OpenPayload(out IStorage payloadStorage, int index)
        {
            UnsafeHelpers.SkipParamInit(out payloadStorage);

            if ((uint)index >= Package2Header.PayloadCount)
            {
                return(ResultLibHac.ArgumentOutOfRange.Log());
            }

            int offset = _header.Meta.GetPayloadFileOffset(index);
            int size   = (int)_header.Meta.PayloadSizes[index];

            var payloadSubStorage = new SubStorage(_storage, offset, size);

            if (size == 0)
            {
                payloadStorage = payloadSubStorage;
                return(Result.Success);
            }

            byte[] iv = _header.Meta.PayloadIvs[index].Bytes.ToArray();
            payloadStorage = new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true);
            return(Result.Success);
        }
        public Result CreateSecure(GameCardHandle handle, out IStorage storage)
        {
            storage = default;

            if (GameCard.IsGameCardHandleInvalid(handle))
            {
                return(ResultFs.InvalidGameCardHandleOnOpenSecurePartition.Log());
            }

            Span <byte> deviceId  = stackalloc byte[0x10];
            Span <byte> imageHash = stackalloc byte[0x20];

            Result rc = GameCard.GetGameCardDeviceId(deviceId);

            if (rc.IsFailure())
            {
                return(rc);
            }

            rc = GameCard.GetGameCardImageHash(imageHash);
            if (rc.IsFailure())
            {
                return(rc);
            }

            var baseStorage = new ReadOnlyGameCardStorage(GameCard, handle, deviceId, imageHash);

            rc = GameCard.GetCardInfo(out GameCardInfo cardInfo, handle);
            if (rc.IsFailure())
            {
                return(rc);
            }

            storage = new SubStorage(baseStorage, cardInfo.SecureAreaOffset, cardInfo.SecureAreaSize);
            return(Result.Success);
        }
Example #10
0
        public void Write_SingleBlock_CanReadBack()
        {
            byte[] buffer        = new byte[0x18000];
            byte[] workBuffer    = new byte[0x18000];
            var    bufferManager = new FileSystemBufferManager();

            Assert.Success(bufferManager.Initialize(5, buffer, 0x4000, workBuffer));

            byte[] storageBuffer = new byte[0x80000];
            var    baseStorage   = new SubStorage(new MemoryStorage(storageBuffer), 0, storageBuffer.Length);

            var bufferedStorage = new BufferedStorage();

            Assert.Success(bufferedStorage.Initialize(baseStorage, bufferManager, 0x4000, 4));

            byte[] writeBuffer = new byte[0x400];
            byte[] readBuffer  = new byte[0x400];

            writeBuffer.AsSpan().Fill(0xAA);
            Assert.Success(bufferedStorage.Write(0x10000, writeBuffer));
            Assert.Success(bufferedStorage.Read(0x10000, readBuffer));

            Assert.Equal(writeBuffer, readBuffer);
        }
Example #11
0
        /// <summary>
        /// Compare two result files.
        /// </summary>
        /// <param name="expectedResult">The expected result file.</param>
        /// <param name="actualResult">The actual result file.</param>
        /// <param name="tables">The list of tables to compare</param>
        /// <returns>Any differences found.</returns>
        public static ArrayList CompareResults(string expectedResult, string actualResult, params string[] tables)
        {
            ArrayList differences = new ArrayList();
            Output    targetOutput;
            Output    updatedOutput;

            expectedResult = Environment.ExpandEnvironmentVariables(expectedResult);
            actualResult   = Environment.ExpandEnvironmentVariables(actualResult);

            OutputType outputType;
            string     extension = Path.GetExtension(expectedResult);

            if (String.Compare(extension, ".msi", true, CultureInfo.InvariantCulture) == 0)
            {
                outputType = OutputType.Product;
            }
            else if (String.Compare(extension, ".msm", true, CultureInfo.InvariantCulture) == 0)
            {
                outputType = OutputType.Module;
            }
            else if (String.Compare(extension, ".msp", true, CultureInfo.InvariantCulture) == 0)
            {
                outputType = OutputType.Patch;
            }
            else if (String.Compare(extension, ".mst", true, CultureInfo.InvariantCulture) == 0)
            {
                outputType = OutputType.Transform;
            }
            else if (String.Compare(extension, ".pcp", true, CultureInfo.InvariantCulture) == 0)
            {
                outputType = OutputType.PatchCreation;
            }
            else if (String.Compare(extension, ".wixout", true, CultureInfo.InvariantCulture) == 0)
            {
                outputType = OutputType.Unknown;
            }
            else
            {
                throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot determine the type of msi database file based on file extension '{0}'.", extension));
            }

            if (outputType != OutputType.Unknown)
            {
                Unbinder unbinder = new Unbinder();
                unbinder.SuppressDemodularization = true;

                targetOutput  = unbinder.Unbind(expectedResult, outputType, null);
                updatedOutput = unbinder.Unbind(actualResult, outputType, null);
            }
            else
            {
                targetOutput  = Output.Load(expectedResult, false, false);
                updatedOutput = Output.Load(actualResult, false, false);
            }

            differences.AddRange(CompareOutput(targetOutput, updatedOutput, tables));

            // If the Output type is a Patch, then compare the patch's transforms
            if (outputType == OutputType.Patch)
            {
                // Compare transforms
                foreach (SubStorage targetTransform in targetOutput.SubStorages)
                {
                    SubStorage updatedTransform = null;

                    // Find the same transform in the other patch
                    foreach (SubStorage transform in updatedOutput.SubStorages)
                    {
                        if (transform.Name == targetTransform.Name)
                        {
                            updatedTransform = transform;
                            break;
                        }
                    }

                    if (null != updatedTransform)
                    {
                        // Both patch's have this transform
                        ArrayList transformDifferences = Verifier.CompareOutput(targetTransform.Data, updatedTransform.Data);

                        // add a description of the transforms being compared
                        if (0 < transformDifferences.Count)
                        {
                            transformDifferences.Insert(0, String.Concat("Differences found while comparing the transform ", targetTransform.Name, " from the two patches"));
                            differences.AddRange(transformDifferences);
                        }
                    }
                    else
                    {
                        differences.Add(String.Format("The {0} tranform has been dropped", targetTransform.Name));
                    }
                }

                // Check if the updated patch has had transforms added
                foreach (SubStorage updatedTransform in updatedOutput.SubStorages)
                {
                    SubStorage targetTransform = null;
                    foreach (SubStorage transform in targetOutput.SubStorages)
                    {
                        if (transform.Name == updatedTransform.Name)
                        {
                            targetTransform = transform;
                            break;
                        }
                    }

                    if (targetTransform == null)
                    {
                        differences.Add(String.Format("The {0} tranform has been added", updatedTransform.Name));
                    }
                }
            }

            // add a description of the files being compared
            if (0 < differences.Count)
            {
                differences.Insert(0, "Differences found while comparing:");
                differences.Insert(1, expectedResult);
                differences.Insert(2, actualResult);
            }

            return(differences);
        }
Example #12
0
        /// <summary>
        /// Read the encrypted section of a Mariko Package1 and try to decrypt it.
        /// </summary>
        /// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
        /// <see cref="ResultLibHac.InvalidPackage1MarikoBodySize"/>: The package1 is
        /// too small or the size in the OEM header is incorrect.</returns>
        private Result InitMarikoBodyStorage()
        {
            // Body must be large enough to hold at least one metadata struct
            if (MarikoOemHeader.Size < Unsafe.SizeOf <Package1MetaData>())
            {
                return(ResultLibHac.InvalidPackage1MarikoBodySize.Log());
            }

            // Verify the body storage size is not smaller than the size in the header
            Result rc = BaseStorage.GetSize(out long totalSize);

            if (rc.IsFailure())
            {
                return(rc);
            }

            long bodySize = totalSize - Unsafe.SizeOf <Package1MarikoOemHeader>();

            if (bodySize < MarikoOemHeader.Size)
            {
                return(ResultLibHac.InvalidPackage1MarikoBodySize.Log());
            }

            // Create body SubStorage and metadata buffers
            var bodySubStorage = new SubStorage(BaseStorage, Unsafe.SizeOf <Package1MarikoOemHeader>(), bodySize);

            Span <Package1MetaData> metaData  = stackalloc Package1MetaData[2];
            Span <byte>             metaData1 = SpanHelpers.AsByteSpan(ref metaData[0]);
            Span <byte>             metaData2 = SpanHelpers.AsByteSpan(ref metaData[1]);

            // Read both the plaintext metadata and encrypted metadata
            rc = bodySubStorage.Read(0, MemoryMarshal.Cast <Package1MetaData, byte>(metaData));
            if (rc.IsFailure())
            {
                return(rc);
            }

            // Set the body storage and decrypted metadata
            _metaData   = metaData[0];
            BodyStorage = bodySubStorage;

            // The plaintext metadata is followed by an encrypted copy
            // If these two match then the body is already decrypted
            IsDecrypted = metaData1.SequenceEqual(metaData2);

            if (IsDecrypted)
            {
                return(Result.Success);
            }

            // If encrypted, check if the body can be decrypted
            Crypto.Aes.DecryptCbc128(metaData2, metaData2, KeySet.MarikoBek, _metaData.Iv);
            IsDecrypted = metaData2.SequenceEqual(SpanHelpers.AsByteSpan(ref _metaData));

            // Get a decrypted body storage if we have the correct key
            if (IsDecrypted)
            {
                var decStorage = new AesCbcStorage(bodySubStorage, KeySet.MarikoBek, _metaData.Iv, true);
                BodyStorage = new CachedStorage(decStorage, 0x4000, 1, true);
            }

            return(Result.Success);
        }