From 6213111af9be217552aac7a7aacb7fac26e95e18 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Fri, 12 Oct 2018 17:52:15 -0500 Subject: [PATCH] Add option to verify SwitchFS and SD cards --- LibHac/BktrCryptoStream.cs | 6 +- ...HierarchicalIntegrityVerificationStream.cs | 2 +- LibHac/Nca.cs | 35 ++++++++--- hactoolnet/ProcessSwitchFs.cs | 61 +++++++++++++++++++ 4 files changed, 93 insertions(+), 11 deletions(-) diff --git a/LibHac/BktrCryptoStream.cs b/LibHac/BktrCryptoStream.cs index 7e415c37..31ed4a96 100644 --- a/LibHac/BktrCryptoStream.cs +++ b/LibHac/BktrCryptoStream.cs @@ -78,8 +78,6 @@ namespace LibHac int bytesToRead = (int)Math.Min(CurrentEntry.OffsetEnd - Position, count); int bytesRead = base.Read(buffer, outPos, bytesToRead); - if (bytesRead == 0) break; - outPos += bytesRead; totalBytesRead += bytesRead; count -= bytesRead; @@ -89,6 +87,10 @@ namespace LibHac CurrentEntry = CurrentEntry.Next; UpdateCounterSubsection(CurrentEntry.Counter); } + else if (bytesRead == 0) + { + break; + } } return totalBytesRead; diff --git a/LibHac/HierarchicalIntegrityVerificationStream.cs b/LibHac/HierarchicalIntegrityVerificationStream.cs index c995d018..c6b2d9d5 100644 --- a/LibHac/HierarchicalIntegrityVerificationStream.cs +++ b/LibHac/HierarchicalIntegrityVerificationStream.cs @@ -62,7 +62,7 @@ namespace LibHac { if (validities[i] == Validity.Unchecked) { - levelStream.Position = levelStream.SectorSize * i; + levelStream.Position = (long)levelStream.SectorSize * i; levelStream.Read(buffer, 0, buffer.Length, IntegrityCheckLevel.IgnoreOnInvalid); } diff --git a/LibHac/Nca.cs b/LibHac/Nca.cs index 83ea1ab2..8b50139a 100644 --- a/LibHac/Nca.cs +++ b/LibHac/Nca.cs @@ -478,33 +478,52 @@ namespace LibHac } } - public static void VerifySection(this Nca nca, int index, IProgressReport logger = null) + public static Validity VerifyNca(this Nca nca, IProgressReport logger = null, bool quiet = false) + { + for (int i = 0; i < 3; i++) + { + if (nca.Sections[i] != null) + { + Validity sectionValidity = VerifySection(nca, i, logger, quiet); + + if (sectionValidity == Validity.Invalid) return Validity.Invalid; + } + } + + return Validity.Valid; + } + + public static Validity VerifySection(this Nca nca, int index, IProgressReport logger = null, bool quiet = false) { if (nca.Sections[index] == null) throw new ArgumentOutOfRangeException(nameof(index)); NcaSection sect = nca.Sections[index]; NcaHashType hashType = sect.Header.HashType; - if (hashType != NcaHashType.Sha256 && hashType != NcaHashType.Ivfc) return; + if (hashType != NcaHashType.Sha256 && hashType != NcaHashType.Ivfc) return Validity.Unchecked; HierarchicalIntegrityVerificationStream stream = nca.OpenHashedSection(index, IntegrityCheckLevel.IgnoreOnInvalid); - if (stream == null) return; + if (stream == null) return Validity.Unchecked; - logger?.LogMessage($"Verifying section {index}..."); + if (!quiet) logger?.LogMessage($"Verifying section {index}..."); for (int i = 0; i < stream.Levels.Length - 1; i++) { - logger?.LogMessage($" Verifying Hash Level {i}..."); - Validity result = stream.ValidateLevel(i, true, logger); + if (!quiet) logger?.LogMessage($" Verifying Hash Level {i}..."); + Validity levelValidity = stream.ValidateLevel(i, true, logger); if (hashType == NcaHashType.Ivfc) { - sect.Header.IvfcInfo.LevelHeaders[i].HashValidity = result; + sect.Header.IvfcInfo.LevelHeaders[i].HashValidity = levelValidity; } else if (hashType == NcaHashType.Sha256 && i == stream.Levels.Length - 2) { - sect.Header.Sha256Info.HashValidity = result; + sect.Header.Sha256Info.HashValidity = levelValidity; } + + if (levelValidity == Validity.Invalid) return Validity.Invalid; } + + return Validity.Valid; } } } diff --git a/hactoolnet/ProcessSwitchFs.cs b/hactoolnet/ProcessSwitchFs.cs index e1e88ae9..ab1844d4 100644 --- a/hactoolnet/ProcessSwitchFs.cs +++ b/hactoolnet/ProcessSwitchFs.cs @@ -119,6 +119,67 @@ namespace hactoolnet { ExportSdSaves(ctx, switchFs); } + + if (ctx.Options.Validate) + { + ValidateSwitchFs(ctx, switchFs); + } + } + + private static void ValidateSwitchFs(Context ctx, SwitchFs switchFs) + { + if (ctx.Options.TitleId != 0) + { + ulong id = ctx.Options.TitleId; + + if (!switchFs.Titles.TryGetValue(id, out Title title)) + { + ctx.Logger.LogMessage($"Could not find title {id:X16}"); + return; + } + + ValidateTitle(ctx, title, ""); + + return; + } + + foreach (Application app in switchFs.Applications.Values) + { + ctx.Logger.LogMessage($"Checking {app.Name}..."); + + Title mainTitle = app.Patch ?? app.Main; + + if (mainTitle != null) + { + ValidateTitle(ctx, mainTitle, "Main title"); + } + + foreach (Title title in app.AddOnContent) + { + ValidateTitle(ctx, title, "Add-on content"); + } + } + } + + private static void ValidateTitle(Context ctx, Title title, string caption) + { + try + { + ctx.Logger.LogMessage($" {caption} {title.Id:x16}"); + + foreach (Nca nca in title.Ncas) + { + ctx.Logger.LogMessage($" {nca.Header.ContentType.ToString()}"); + + Validity validity = nca.VerifyNca(ctx.Logger, true); + + ctx.Logger.LogMessage($" {validity.ToString()}"); + } + } + catch (Exception ex) + { + ctx.Logger.LogMessage($"Error processing title {title.Id:x16}:\n{ex.Message}"); + } } private static void SaveTitle(Context ctx, SwitchFs switchFs)