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); }
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)); }
/// <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); }
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); }
/// <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); }
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); }
/// <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); }
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); }
/// <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); }
/// <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); }