From 0a8fb8a5c00b9778414647fe4492668d168c17c5 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 3 Nov 2021 15:40:43 -0700 Subject: [PATCH] Update package1 and package2 readers to use SharedRef --- src/LibHac/Boot/Package2StorageReader.cs | 65 +++++----- .../Kernel/InitialProcessBinaryReader.cs | 40 +++--- src/LibHac/Kernel/KipReader.cs | 33 ++--- src/LibHac/Kip.cs | 5 +- src/hactoolnet/ProcessKip.cs | 47 +++---- src/hactoolnet/ProcessPackage.cs | 117 +++++++++--------- 6 files changed, 163 insertions(+), 144 deletions(-) diff --git a/src/LibHac/Boot/Package2StorageReader.cs b/src/LibHac/Boot/Package2StorageReader.cs index c23a02e1..125a0cde 100644 --- a/src/LibHac/Boot/Package2StorageReader.cs +++ b/src/LibHac/Boot/Package2StorageReader.cs @@ -13,33 +13,38 @@ namespace LibHac.Boot /// /// Parses a package2 file and opens the payloads within. /// - public class Package2StorageReader + public class Package2StorageReader : IDisposable { private const int KernelPayloadIndex = 0; private const int IniPayloadIndex = 1; - private IStorage _storage; + private SharedRef _storage; private Package2Header _header; private KeySet _keySet; private Crypto.AesKey _key; public ref readonly Package2Header Header => ref _header; + public void Dispose() + { + _storage.Destroy(); + } + /// /// Initializes the . /// /// The keyset to use for decrypting the package. /// An of the encrypted package2. /// The of the operation. - public Result Initialize(KeySet keySet, IStorage storage) + public Result Initialize(KeySet keySet, in SharedRef storage) { - Result rc = storage.Read(0, SpanHelpers.AsByteSpan(ref _header)); + Result rc = storage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header)); if (rc.IsFailure()) return rc; _key = keySet.Package2Keys[_header.Meta.KeyGeneration]; DecryptHeader(_key, ref _header.Meta, ref _header.Meta); - _storage = storage; + _storage.SetByCopy(in storage); _keySet = keySet; return Result.Success; } @@ -47,14 +52,12 @@ namespace LibHac.Boot /// /// Opens a decrypted of one of the payloads in the package. /// - /// If the method returns successfully, contains an + /// If the method returns successfully, contains an /// of the specified payload. /// The index of the payload to get. Must me less than /// The of the operation. - public Result OpenPayload(out IStorage payloadStorage, int index) + public Result OpenPayload(ref UniqueRef outPayloadStorage, int index) { - UnsafeHelpers.SkipParamInit(out payloadStorage); - if ((uint)index >= Package2Header.PayloadCount) return ResultLibHac.ArgumentOutOfRange.Log(); @@ -65,53 +68,52 @@ namespace LibHac.Boot if (size == 0) { - payloadStorage = payloadSubStorage; + outPayloadStorage.Reset(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); + outPayloadStorage.Reset(new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true)); return Result.Success; } /// /// Opens an of the kernel payload. /// - /// If the method returns successfully, contains an + /// If the method returns successfully, contains an /// of the kernel payload. /// The of the operation. - public Result OpenKernel(out IStorage kernelStorage) + public Result OpenKernel(ref UniqueRef outKernelStorage) { - return OpenPayload(out kernelStorage, KernelPayloadIndex); + return OpenPayload(ref outKernelStorage, KernelPayloadIndex); } /// /// Opens an of the initial process binary. If the binary is embedded in /// the kernel, this method will attempt to locate and return the embedded binary. /// - /// If the method returns successfully, contains an + /// If the method returns successfully, contains an /// of the initial process binary. /// The of the operation. - public Result OpenIni(out IStorage iniStorage) + public Result OpenIni(ref UniqueRef outIniStorage) { if (HasIniPayload()) { - return OpenPayload(out iniStorage, IniPayloadIndex); + return OpenPayload(ref outIniStorage, IniPayloadIndex); } // Ini is embedded in the kernel - UnsafeHelpers.SkipParamInit(out iniStorage); - - Result rc = OpenKernel(out IStorage kernelStorage); + using var kernelStorage = new UniqueRef(); + Result rc = OpenKernel(ref kernelStorage.Ref()); if (rc.IsFailure()) return rc; - if (!IniExtract.TryGetIni1Offset(out int offset, out int size, kernelStorage)) + if (!IniExtract.TryGetIni1Offset(out int offset, out int size, kernelStorage.Get)) { // Unable to find the ini. Could be a new, unsupported layout. return ResultLibHac.NotImplemented.Log(); } - iniStorage = new SubStorage(kernelStorage, offset, size); + outIniStorage.Reset(new SubStorage(kernelStorage.Release(), offset, size)); return Result.Success; } @@ -140,7 +142,7 @@ namespace LibHac.Boot Unsafe.SkipInit(out Package2Meta meta); Span metaBytes = SpanHelpers.AsByteSpan(ref meta); - Result rc = _storage.Read(Package2Header.SignatureSize, metaBytes); + Result rc = _storage.Get.Read(Package2Header.SignatureSize, metaBytes); if (rc.IsFailure()) return rc; return _header.VerifySignature(_keySet.Package2SigningKeyParams.Modulus, metaBytes); @@ -209,10 +211,10 @@ namespace LibHac.Boot /// /// Opens a decrypted of the entire package. /// - /// If the method returns successfully, contains a decrypted + /// If the method returns successfully, contains a decrypted /// of the package. /// The of the operation. - public Result OpenDecryptedPackage(out IStorage packageStorage) + public Result OpenDecryptedPackage(ref UniqueRef outPackageStorage) { var storages = new List(4); @@ -239,17 +241,14 @@ namespace LibHac.Boot if (_header.Meta.PayloadSizes[i] == 0) continue; - Result rc = OpenPayload(out IStorage payloadStorage, i); - if (rc.IsFailure()) - { - UnsafeHelpers.SkipParamInit(out packageStorage); - return rc; - } + using var payloadStorage = new UniqueRef(); + Result rc = OpenPayload(ref payloadStorage.Ref(), i); + if (rc.IsFailure()) return rc.Miss(); - storages.Add(payloadStorage); + storages.Add(payloadStorage.Release()); } - packageStorage = new ConcatenationStorage(storages, true); + outPackageStorage.Reset(new ConcatenationStorage(storages, true)); return Result.Success; } diff --git a/src/LibHac/Kernel/InitialProcessBinaryReader.cs b/src/LibHac/Kernel/InitialProcessBinaryReader.cs index 041ee2e9..4f8f81eb 100644 --- a/src/LibHac/Kernel/InitialProcessBinaryReader.cs +++ b/src/LibHac/Kernel/InitialProcessBinaryReader.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Diag; @@ -6,32 +7,37 @@ using LibHac.Fs; namespace LibHac.Kernel { - public class InitialProcessBinaryReader + public class InitialProcessBinaryReader : IDisposable { internal const uint ExpectedMagic = 0x31494E49; // INI1 private const int MaxProcessCount = 80; - private IStorage _storage; + private SharedRef _storage; private IniHeader _header; private (int offset, int size)[] _offsets; public ref readonly IniHeader Header => ref _header; public int ProcessCount => _header.ProcessCount; - public Result Initialize(IStorage binaryStorage) + public void Dispose() { - if (binaryStorage is null) + _storage.Destroy(); + } + + public Result Initialize(in SharedRef binaryStorage) + { + if (!binaryStorage.HasValue) return ResultLibHac.NullArgument.Log(); // Verify there's enough data to read the header - Result rc = binaryStorage.GetSize(out long iniSize); + Result rc = binaryStorage.Get.GetSize(out long iniSize); if (rc.IsFailure()) return rc; if (iniSize < Unsafe.SizeOf()) return ResultLibHac.InvalidIniFileSize.Log(); // Read the INI file header and validate some of its values. - rc = binaryStorage.Read(0, SpanHelpers.AsByteSpan(ref _header)); + rc = binaryStorage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header)); if (rc.IsFailure()) return rc; if (_header.Magic != ExpectedMagic) @@ -45,36 +51,40 @@ namespace LibHac.Kernel rc = GetKipOffsets(out _offsets, binaryStorage, _header.ProcessCount); if (rc.IsFailure()) return rc; - _storage = binaryStorage; + _storage.SetByCopy(in binaryStorage); return Result.Success; } - public Result OpenKipStorage(out IStorage storage, int index) + public Result OpenKipStorage(ref UniqueRef outStorage, int index) { - UnsafeHelpers.SkipParamInit(out storage); - if ((uint)index >= _header.ProcessCount) return ResultLibHac.ArgumentOutOfRange.Log(); (int offset, int size) range = _offsets[index]; - storage = new SubStorage(_storage, range.offset, range.size); + outStorage.Reset(new SubStorage(in _storage, range.offset, range.size)); return Result.Success; } - private static Result GetKipOffsets(out (int offset, int size)[] kipOffsets, IStorage iniStorage, + private static Result GetKipOffsets(out (int offset, int size)[] kipOffsets, in SharedRef iniStorage, int processCount) { Assert.SdkRequiresLessEqual(processCount, MaxProcessCount); UnsafeHelpers.SkipParamInit(out kipOffsets); + Result rc = iniStorage.Get.GetSize(out long iniStorageSize); + if (rc.IsFailure()) return rc.Miss(); + var offsets = new (int offset, int size)[processCount]; int offset = Unsafe.SizeOf(); - var kipReader = new KipReader(); + using var kipReader = new KipReader(); for (int i = 0; i < processCount; i++) { - Result rc = kipReader.Initialize(new SubStorage(iniStorage, offset, int.MaxValue)); + using var kipStorage = + new SharedRef(new SubStorage(in iniStorage, offset, iniStorageSize - offset)); + + rc = kipReader.Initialize(in kipStorage); if (rc.IsFailure()) return rc; int kipSize = kipReader.GetFileSize(); diff --git a/src/LibHac/Kernel/KipReader.cs b/src/LibHac/Kernel/KipReader.cs index a7d2b693..78df5080 100644 --- a/src/LibHac/Kernel/KipReader.cs +++ b/src/LibHac/Kernel/KipReader.cs @@ -8,9 +8,9 @@ using LibHac.Fs; namespace LibHac.Kernel { - public class KipReader + public class KipReader : IDisposable { - private IStorage KipStorage { get; set; } + private SharedRef _kipStorage; private KipHeader _header; @@ -34,48 +34,51 @@ namespace LibHac.Kernel public int AffinityMask => _header.AffinityMask; public int StackSize => _header.StackSize; - public Result Initialize(IStorage kipData) + public void Dispose() { - if (kipData is null) + _kipStorage.Destroy(); + } + + public Result Initialize(in SharedRef kipData) + { + if (!kipData.HasValue) return ResultLibHac.NullArgument.Log(); // Verify there's enough data to read the header - Result rc = kipData.GetSize(out long kipSize); + Result rc = kipData.Get.GetSize(out long kipSize); if (rc.IsFailure()) return rc; if (kipSize < Unsafe.SizeOf()) return ResultLibHac.InvalidKipFileSize.Log(); - rc = kipData.Read(0, SpanHelpers.AsByteSpan(ref _header)); + rc = kipData.Get.Read(0, SpanHelpers.AsByteSpan(ref _header)); if (rc.IsFailure()) return rc; if (!_header.IsValid) return ResultLibHac.InvalidKipMagic.Log(); - KipStorage = kipData; + _kipStorage.SetByCopy(in kipData); return Result.Success; } /// /// Gets the raw input KIP file. /// - /// If the operation returns successfully, an + /// If the operation returns successfully, an /// containing the KIP data. /// The of the operation. - public Result GetRawData(out IStorage kipData) + public Result GetRawData(ref UniqueRef outKipData) { - UnsafeHelpers.SkipParamInit(out kipData); - int kipFileSize = GetFileSize(); - Result rc = KipStorage.GetSize(out long inputFileSize); + Result rc = _kipStorage.Get.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); + outKipData.Reset(new SubStorage(in _kipStorage, 0, kipFileSize)); return Result.Success; } @@ -149,14 +152,14 @@ namespace LibHac.Kernel int offset = CalculateSegmentOffset((int)segment); // Verify the segment offset is in-range - rc = KipStorage.GetSize(out long kipSize); + rc = _kipStorage.Get.GetSize(out long kipSize); if (rc.IsFailure()) return rc; if (kipSize < offset + segmentHeader.FileSize) return ResultLibHac.InvalidKipFileSize.Log(); // Read the segment data. - rc = KipStorage.Read(offset, buffer.Slice(0, segmentHeader.FileSize)); + rc = _kipStorage.Get.Read(offset, buffer.Slice(0, segmentHeader.FileSize)); if (rc.IsFailure()) return rc; // Decompress if necessary. diff --git a/src/LibHac/Kip.cs b/src/LibHac/Kip.cs index c021679a..5d06fe72 100644 --- a/src/LibHac/Kip.cs +++ b/src/LibHac/Kip.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using LibHac.Common; using LibHac.Fs; using LibHac.FsSystem; using LibHac.Kernel; @@ -200,8 +201,10 @@ namespace LibHac for (int i = 0; i < KipCount; i++) { + using var sharedStorage = new SharedRef(Storage.Slice(offset)); + Kips[i] = new KipReader(); - Kips[i].Initialize(Storage.Slice(offset)).ThrowIfFailure(); + Kips[i].Initialize(in sharedStorage).ThrowIfFailure(); offset += Kips[i].GetFileSize(); } diff --git a/src/hactoolnet/ProcessKip.cs b/src/hactoolnet/ProcessKip.cs index 3cc9aa5b..b7ba88fe 100644 --- a/src/hactoolnet/ProcessKip.cs +++ b/src/hactoolnet/ProcessKip.cs @@ -1,4 +1,5 @@ using System.IO; +using LibHac.Common; using LibHac.Fs; using LibHac.FsSystem; using LibHac.Kernel; @@ -9,50 +10,50 @@ namespace hactoolnet { public static void ProcessKip1(Context ctx) { - using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read)) + using var file = new SharedRef(new LocalStorage(ctx.Options.InFile, FileAccess.Read)); + + using var kip = new KipReader(); + kip.Initialize(in file).ThrowIfFailure(); + + if (!string.IsNullOrWhiteSpace(ctx.Options.UncompressedOut)) { - var kip = new KipReader(); - kip.Initialize(file).ThrowIfFailure(); + byte[] uncompressed = new byte[kip.GetUncompressedSize()]; - if (!string.IsNullOrWhiteSpace(ctx.Options.UncompressedOut)) - { - byte[] uncompressed = new byte[kip.GetUncompressedSize()]; + kip.ReadUncompressedKip(uncompressed).ThrowIfFailure(); - kip.ReadUncompressedKip(uncompressed).ThrowIfFailure(); - - File.WriteAllBytes(ctx.Options.UncompressedOut, uncompressed); - } + File.WriteAllBytes(ctx.Options.UncompressedOut, uncompressed); } } public static void ProcessIni1(Context ctx) { - using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read)) - { - string outDir = ctx.Options.OutDir; + using var file = new SharedRef(new LocalStorage(ctx.Options.InFile, FileAccess.Read)); - if (outDir != null) - { - ExtractIni1(file, outDir); - } + string outDir = ctx.Options.OutDir; + + if (outDir != null) + { + ExtractIni1(in file, outDir); } } - public static void ExtractIni1(IStorage iniStorage, string outDir) + public static void ExtractIni1(in SharedRef iniStorage, string outDir) { - var ini1 = new InitialProcessBinaryReader(); + using var ini1 = new InitialProcessBinaryReader(); ini1.Initialize(iniStorage).ThrowIfFailure(); Directory.CreateDirectory(outDir); - var kipReader = new KipReader(); + using var kipReader = new KipReader(); for (int i = 0; i < ini1.ProcessCount; i++) { - ini1.OpenKipStorage(out IStorage kipStorage, i).ThrowIfFailure(); + using var kipStorage = new UniqueRef(); + ini1.OpenKipStorage(ref kipStorage.Ref(), i).ThrowIfFailure(); - kipReader.Initialize(kipStorage).ThrowIfFailure(); + using SharedRef sharedKipStorage = SharedRef.Create(ref kipStorage.Ref()); + kipReader.Initialize(in sharedKipStorage).ThrowIfFailure(); - kipStorage.WriteAllBytes(System.IO.Path.Combine(outDir, $"{kipReader.Name.ToString()}.kip1")); + sharedKipStorage.Get.WriteAllBytes(System.IO.Path.Combine(outDir, $"{kipReader.Name.ToString()}.kip1")); } } } diff --git a/src/hactoolnet/ProcessPackage.cs b/src/hactoolnet/ProcessPackage.cs index ceefaff4..b1cb7515 100644 --- a/src/hactoolnet/ProcessPackage.cs +++ b/src/hactoolnet/ProcessPackage.cs @@ -15,43 +15,42 @@ namespace hactoolnet { public static void ProcessPk11(Context ctx) { - using (var file = new SharedRef(new LocalStorage(ctx.Options.InFile, FileAccess.Read))) + using var file = new SharedRef(new LocalStorage(ctx.Options.InFile, FileAccess.Read)); + + var package1 = new LibHac.Boot.Package1(); + package1.Initialize(ctx.KeySet, in file).ThrowIfFailure(); + + ctx.Logger.LogMessage(package1.Print()); + + string outDir = ctx.Options.OutDir; + + if (package1.IsDecrypted && outDir != null) { - var package1 = new LibHac.Boot.Package1(); - package1.Initialize(ctx.KeySet, in file).ThrowIfFailure(); + Directory.CreateDirectory(outDir); - ctx.Logger.LogMessage(package1.Print()); + IStorage decryptedStorage = package1.OpenDecryptedPackage1Storage(); - string outDir = ctx.Options.OutDir; + WriteFile(decryptedStorage, "Decrypted.bin"); + WriteFile(package1.OpenWarmBootStorage(), "Warmboot.bin"); + WriteFile(package1.OpenNxBootloaderStorage(), "NX_Bootloader.bin"); + WriteFile(package1.OpenSecureMonitorStorage(), "Secure_Monitor.bin"); - if (package1.IsDecrypted && outDir != null) + if (package1.IsMariko) { - Directory.CreateDirectory(outDir); + WriteFile(package1.OpenDecryptedWarmBootStorage(), "Warmboot_Decrypted.bin"); - IStorage decryptedStorage = package1.OpenDecryptedPackage1Storage(); + var marikoOemLoader = new SubStorage(decryptedStorage, Unsafe.SizeOf(), + package1.MarikoOemHeader.Size); - 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(), - package1.MarikoOemHeader.Size); - - WriteFile(marikoOemLoader, "Mariko_OEM_Bootloader.bin"); - } + 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); - } + void WriteFile(IStorage storage, string filename) + { + string path = Path.Combine(outDir, filename); + ctx.Logger.LogMessage($"Writing {path}..."); + storage.WriteAllBytes(path, ctx.Logger); } } @@ -105,43 +104,47 @@ namespace hactoolnet public static void ProcessPk21(Context ctx) { - using (var file = new CachedStorage(new LocalStorage(ctx.Options.InFile, FileAccess.Read), 0x4000, 4, false)) + using var file = new SharedRef(new CachedStorage(new LocalStorage(ctx.Options.InFile, FileAccess.Read), 0x4000, 4, false)); + + using var package2 = new Package2StorageReader(); + package2.Initialize(ctx.KeySet, in file).ThrowIfFailure(); + + ctx.Logger.LogMessage(package2.Print()); + + string outDir = ctx.Options.OutDir; + string iniDir = ctx.Options.Ini1OutDir; + + if (iniDir == null && ctx.Options.ExtractIni1) { - var package2 = new Package2StorageReader(); - package2.Initialize(ctx.KeySet, file).ThrowIfFailure(); + iniDir = Path.Combine(outDir, "INI1"); + } - ctx.Logger.LogMessage(package2.Print()); + if (outDir != null) + { + Directory.CreateDirectory(outDir); - string outDir = ctx.Options.OutDir; - string iniDir = ctx.Options.Ini1OutDir; + using var kernelStorage = new UniqueRef(); + package2.OpenPayload(ref kernelStorage.Ref(), 0).ThrowIfFailure(); + kernelStorage.Get.WriteAllBytes(Path.Combine(outDir, "Kernel.bin"), ctx.Logger); - if (iniDir == null && ctx.Options.ExtractIni1) - { - iniDir = Path.Combine(outDir, "INI1"); - } + using var ini1Storage = new UniqueRef(); + package2.OpenIni(ref ini1Storage.Ref()).ThrowIfFailure(); + ini1Storage.Get.WriteAllBytes(Path.Combine(outDir, "INI1.bin"), ctx.Logger); - if (outDir != null) - { - Directory.CreateDirectory(outDir); + using var decPackageStorage = new UniqueRef(); + package2.OpenDecryptedPackage(ref decPackageStorage.Ref()).ThrowIfFailure(); + decPackageStorage.Get.WriteAllBytes(Path.Combine(outDir, "Decrypted.bin"), ctx.Logger); + } - package2.OpenPayload(out IStorage kernelStorage, 0).ThrowIfFailure(); - kernelStorage.WriteAllBytes(Path.Combine(outDir, "Kernel.bin"), ctx.Logger); + if (iniDir != null) + { + Directory.CreateDirectory(iniDir); - package2.OpenIni(out IStorage ini1Storage).ThrowIfFailure(); - ini1Storage.WriteAllBytes(Path.Combine(outDir, "INI1.bin"), ctx.Logger); + using var ini1Storage = new UniqueRef(); + package2.OpenIni(ref ini1Storage.Ref()).ThrowIfFailure(); - package2.OpenDecryptedPackage(out IStorage decPackageStorage).ThrowIfFailure(); - decPackageStorage.WriteAllBytes(Path.Combine(outDir, "Decrypted.bin"), ctx.Logger); - } - - if (iniDir != null) - { - Directory.CreateDirectory(iniDir); - - package2.OpenIni(out IStorage ini1Storage).ThrowIfFailure(); - - ProcessKip.ExtractIni1(ini1Storage, iniDir); - } + using SharedRef sharedIni1Storage = SharedRef.Create(ref ini1Storage.Ref()); + ProcessKip.ExtractIni1(in sharedIni1Storage, iniDir); } }