mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Fix opening decrypted NCAs
Fixes an issue where the decrypted NCA could be incorrect if the first section didn't start at offset 0x4000
This commit is contained in:
parent
a1bdadb89b
commit
56c4554d81
3 changed files with 82 additions and 13 deletions
63
src/LibHac/IO/ConcatenationStorageBuilder.cs
Normal file
63
src/LibHac/IO/ConcatenationStorageBuilder.cs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace LibHac.IO
|
||||||
|
{
|
||||||
|
public class ConcatenationStorageBuilder
|
||||||
|
{
|
||||||
|
private List<ConcatenationStorageSegment> Segments { get; }
|
||||||
|
|
||||||
|
public ConcatenationStorageBuilder()
|
||||||
|
{
|
||||||
|
Segments = new List<ConcatenationStorageSegment>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcatenationStorageBuilder(IEnumerable<ConcatenationStorageSegment> segments)
|
||||||
|
{
|
||||||
|
Segments = segments.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(IStorage storage, long offset)
|
||||||
|
{
|
||||||
|
Segments.Add(new ConcatenationStorageSegment(storage, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcatenationStorage Build()
|
||||||
|
{
|
||||||
|
List<ConcatenationStorageSegment> segments = Segments.OrderBy(x => x.Offset).ToList();
|
||||||
|
var sources = new List<IStorage>();
|
||||||
|
|
||||||
|
long offset = 0;
|
||||||
|
|
||||||
|
foreach (ConcatenationStorageSegment segment in segments)
|
||||||
|
{
|
||||||
|
long paddingNeeded = segment.Offset - offset;
|
||||||
|
|
||||||
|
if (paddingNeeded < 0) throw new InvalidDataException("Builder has segments that overlap.");
|
||||||
|
|
||||||
|
if (paddingNeeded > 0)
|
||||||
|
{
|
||||||
|
sources.Add(new NullStorage(paddingNeeded));
|
||||||
|
}
|
||||||
|
|
||||||
|
sources.Add(segment.Storage);
|
||||||
|
offset = segment.Offset + segment.Storage.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConcatenationStorage(sources, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConcatenationStorageSegment
|
||||||
|
{
|
||||||
|
public IStorage Storage { get; }
|
||||||
|
public long Offset { get; }
|
||||||
|
|
||||||
|
public ConcatenationStorageSegment(IStorage storage, long offset)
|
||||||
|
{
|
||||||
|
Storage = storage;
|
||||||
|
Offset = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using LibHac.IO.RomFs;
|
using LibHac.IO.RomFs;
|
||||||
|
@ -314,14 +313,15 @@ namespace LibHac.IO.NcaUtils
|
||||||
|
|
||||||
public IStorage OpenDecryptedNca()
|
public IStorage OpenDecryptedNca()
|
||||||
{
|
{
|
||||||
var list = new List<IStorage> { OpenHeaderStorage() };
|
var builder = new ConcatenationStorageBuilder();
|
||||||
|
builder.Add(OpenHeaderStorage(), 0);
|
||||||
|
|
||||||
foreach (NcaSection section in Sections.Where(x => x != null).OrderBy(x => x.Offset))
|
foreach (NcaSection section in Sections.Where(x => x != null))
|
||||||
{
|
{
|
||||||
list.Add(OpenRawStorage(section.SectionNum));
|
builder.Add(OpenRawStorage(section.SectionNum), section.Offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ConcatenationStorage(list, true);
|
return builder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private NcaHeader DecryptHeader()
|
private NcaHeader DecryptHeader()
|
||||||
|
@ -336,11 +336,13 @@ namespace LibHac.IO.NcaUtils
|
||||||
|
|
||||||
private CachedStorage OpenHeaderStorage()
|
private CachedStorage OpenHeaderStorage()
|
||||||
{
|
{
|
||||||
int size = 0x4000;
|
long size = 0xc00;
|
||||||
|
|
||||||
// Support reading headers that are only 0xC00 bytes long, but still return
|
// Encrypted portion continues until the first section
|
||||||
// the entire header if available.
|
if (Sections.Any(x => x != null))
|
||||||
if (BaseStorage.Length >= 0xC00 && BaseStorage.Length < 0x4000) size = 0xC00;
|
{
|
||||||
|
size = Sections.Where(x => x != null).Min(x => x.Offset);
|
||||||
|
}
|
||||||
|
|
||||||
return new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, size), Keyset.HeaderKey, 0x200, true), 1, true);
|
return new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, size), Keyset.HeaderKey, 0x200, true), 1, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,15 @@ namespace LibHac.IO.NcaUtils
|
||||||
{
|
{
|
||||||
public static class NcaExtensions
|
public static class NcaExtensions
|
||||||
{
|
{
|
||||||
public static IStorage OpenStorage(this Nca nca, int index, IntegrityCheckLevel integrityCheckLevel, bool openRaw)
|
public static IStorage OpenStorage(this Nca nca, int index, IntegrityCheckLevel integrityCheckLevel,
|
||||||
|
bool openRaw)
|
||||||
{
|
{
|
||||||
if (openRaw) return nca.OpenRawStorage(index);
|
if (openRaw) return nca.OpenRawStorage(index);
|
||||||
return nca.OpenStorage(index, integrityCheckLevel);
|
return nca.OpenStorage(index, integrityCheckLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IStorage OpenStorage(this Nca nca, NcaSectionType type, IntegrityCheckLevel integrityCheckLevel, bool openRaw)
|
public static IStorage OpenStorage(this Nca nca, NcaSectionType type, IntegrityCheckLevel integrityCheckLevel,
|
||||||
|
bool openRaw)
|
||||||
{
|
{
|
||||||
if (openRaw) return nca.OpenRawStorage(type);
|
if (openRaw) return nca.OpenRawStorage(type);
|
||||||
return nca.OpenStorage(type, integrityCheckLevel);
|
return nca.OpenStorage(type, integrityCheckLevel);
|
||||||
|
@ -23,7 +25,8 @@ namespace LibHac.IO.NcaUtils
|
||||||
.WriteAllBytes(filename, logger);
|
.WriteAllBytes(filename, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExtractSection(this Nca nca, int index, string outputDir, IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, IProgressReport logger = null)
|
public static void ExtractSection(this Nca nca, int index, string outputDir,
|
||||||
|
IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, IProgressReport logger = null)
|
||||||
{
|
{
|
||||||
if (index < 0 || index > 3) throw new IndexOutOfRangeException();
|
if (index < 0 || index > 3) throw new IndexOutOfRangeException();
|
||||||
if (!nca.SectionIsDecryptable(index)) return;
|
if (!nca.SectionIsDecryptable(index)) return;
|
||||||
|
@ -55,7 +58,8 @@ namespace LibHac.IO.NcaUtils
|
||||||
NcaHashType hashType = sect.Header.HashType;
|
NcaHashType hashType = sect.Header.HashType;
|
||||||
if (hashType != NcaHashType.Sha256 && hashType != NcaHashType.Ivfc) return Validity.Unchecked;
|
if (hashType != NcaHashType.Sha256 && hashType != NcaHashType.Ivfc) return Validity.Unchecked;
|
||||||
|
|
||||||
var stream = nca.OpenStorage(index, IntegrityCheckLevel.IgnoreOnInvalid, false) as HierarchicalIntegrityVerificationStorage;
|
var stream = nca.OpenStorage(index, IntegrityCheckLevel.IgnoreOnInvalid, false)
|
||||||
|
as HierarchicalIntegrityVerificationStorage;
|
||||||
if (stream == null) return Validity.Unchecked;
|
if (stream == null) return Validity.Unchecked;
|
||||||
|
|
||||||
if (!quiet) logger?.LogMessage($"Verifying section {index}...");
|
if (!quiet) logger?.LogMessage($"Verifying section {index}...");
|
||||||
|
|
Loading…
Reference in a new issue