Add Partition FS extraction (#12)

Add Partition FS extraction + misc. changes
This commit is contained in:
jonnysp 2018-10-03 22:14:23 +02:00 committed by Alex Barney
parent e798a8dd45
commit d12f419d8d
11 changed files with 94 additions and 86 deletions

View file

@ -11,12 +11,14 @@ namespace LibHac
internal const int Aes128Size = 0x10;
internal const int Sha256DigestSize = 0x20;
public static bool CheckMemoryHashTable(byte[] data, byte[] hash)
public static Validity CheckMemoryHashTable(byte[] data, byte[] hash, int offset, int count)
{
Validity comp;
using (SHA256 sha = SHA256.Create())
{
return Util.ArraysEqual(hash, sha.ComputeHash(data));
comp = Util.ArraysEqual(hash, sha.ComputeHash(data, offset, count)) ? Validity.Valid : Validity.Invalid;
}
return comp;
}
public static void DecryptEcb(byte[] key, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)

View file

@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using LibHac.Streams;
using LibHac.XTSSharp;
@ -192,7 +191,7 @@ namespace LibHac
var reader = new BinaryReader(new MemoryStream(headerBytes));
Header = NcaHeader.Read(reader);
Header = new NcaHeader(reader);
}
private void DecryptKeyArea(Keyset keyset)
@ -277,7 +276,6 @@ namespace LibHac
NcaSection sect = Sections[index];
byte[] expected = null;
byte[] actual;
long offset = 0;
long size = 0;
@ -310,15 +308,8 @@ namespace LibHac
stream.Position = offset;
stream.Read(hashTable, 0, hashTable.Length);
using (SHA256 hash = SHA256.Create())
{
actual = hash.ComputeHash(hashTable);
}
Validity validity = Util.ArraysEqual(expected, actual) ? Validity.Valid : Validity.Invalid;
sect.SuperblockHashValidity = validity;
if (sect.Type == SectionType.Romfs) sect.Romfs.IvfcLevels[0].HashValidity = validity;
sect.SuperblockHashValidity = Crypto.CheckMemoryHashTable(hashTable, expected, 0, hashTable.Length);
if (sect.Type == SectionType.Romfs) sect.Romfs.IvfcLevels[0].HashValidity = sect.SuperblockHashValidity;
}
public void VerifySection(int index, IProgressReport logger = null)
@ -362,8 +353,7 @@ namespace LibHac
var table = new byte[level.HashSize];
section.Position = level.HashOffset;
section.Read(table, 0, table.Length);
level.HashValidity =
VerifyHashTable(section, table, level.DataOffset, level.DataSize, level.HashBlockSize, true, logger);
level.HashValidity = VerifyHashTable(section, table, level.DataOffset, level.DataSize, level.HashBlockSize, true, logger);
}
}
@ -377,26 +367,23 @@ namespace LibHac
section.Position = dataOffset;
logger?.SetTotal(blockCount);
using (SHA256 sha256 = SHA256.Create())
for (long i = 0; i < blockCount; i++)
{
for (long i = 0; i < blockCount; i++)
var remaining = (dataLen - i * blockSize);
if (remaining < blockSize)
{
long remaining = dataLen - i * blockSize;
if (remaining < blockSize)
{
Array.Clear(currentBlock, 0, currentBlock.Length);
if (!isFinalBlockFull) curBlockSize = (int)remaining;
}
Array.Copy(hashTable, i * hashSize, expectedHash, 0, hashSize);
section.Read(currentBlock, 0, curBlockSize);
byte[] actualHash = sha256.ComputeHash(currentBlock, 0, curBlockSize);
if (!Util.ArraysEqual(expectedHash, actualHash))
{
return Validity.Invalid;
}
logger?.ReportAdd(1);
Array.Clear(currentBlock, 0, currentBlock.Length);
if (!isFinalBlockFull) curBlockSize = (int)remaining;
}
Array.Copy(hashTable, i * hashSize, expectedHash, 0, hashSize);
section.Read(currentBlock, 0, curBlockSize);
if (Crypto.CheckMemoryHashTable(currentBlock, expectedHash, 0, curBlockSize) == Validity.Invalid)
{
return Validity.Invalid;
}
logger?.ReportAdd(1);
}
return Validity.Valid;

View file

@ -25,50 +25,47 @@ namespace LibHac
public NcaFsHeader[] FsHeaders = new NcaFsHeader[4];
public static NcaHeader Read(BinaryReader reader)
public NcaHeader(BinaryReader reader)
{
var head = new NcaHeader();
head.Signature1 = reader.ReadBytes(0x100);
head.Signature2 = reader.ReadBytes(0x100);
head.Magic = reader.ReadAscii(4);
if (head.Magic != "NCA3") throw new InvalidDataException("Not an NCA3 file");
head.Distribution = (DistributionType)reader.ReadByte();
head.ContentType = (ContentType)reader.ReadByte();
head.CryptoType = reader.ReadByte();
head.KaekInd = reader.ReadByte();
head.NcaSize = reader.ReadUInt64();
head.TitleId = reader.ReadUInt64();
Signature1 = reader.ReadBytes(0x100);
Signature2 = reader.ReadBytes(0x100);
Magic = reader.ReadAscii(4);
if (Magic != "NCA3") throw new InvalidDataException("Not an NCA3 file");
Distribution = (DistributionType)reader.ReadByte();
ContentType = (ContentType)reader.ReadByte();
CryptoType = reader.ReadByte();
KaekInd = reader.ReadByte();
NcaSize = reader.ReadUInt64();
TitleId = reader.ReadUInt64();
reader.BaseStream.Position += 4;
head.SdkVersion = new TitleVersion(reader.ReadUInt32());
head.CryptoType2 = reader.ReadByte();
SdkVersion = new TitleVersion(reader.ReadUInt32());
CryptoType2 = reader.ReadByte();
reader.BaseStream.Position += 0xF;
head.RightsId = reader.ReadBytes(0x10);
RightsId = reader.ReadBytes(0x10);
for (int i = 0; i < 4; i++)
{
head.SectionEntries[i] = new NcaSectionEntry(reader);
SectionEntries[i] = new NcaSectionEntry(reader);
}
for (int i = 0; i < 4; i++)
{
head.SectionHashes[i] = reader.ReadBytes(0x20);
SectionHashes[i] = reader.ReadBytes(0x20);
}
for (int i = 0; i < 4; i++)
{
head.EncryptedKeys[i] = reader.ReadBytes(0x10);
EncryptedKeys[i] = reader.ReadBytes(0x10);
}
reader.BaseStream.Position += 0xC0;
for (int i = 0; i < 4; i++)
{
head.FsHeaders[i] = new NcaFsHeader(reader);
FsHeaders[i] = new NcaFsHeader(reader);
}
return head;
}
}

View file

@ -137,18 +137,12 @@ namespace LibHac
}
if (Type == PfsType.Hfs0) {
if (Type == PfsType.Hfs0)
{
for (int i = 0; i < NumFiles; i++)
{
reader.BaseStream.Position = HeaderSize + Files[i].Offset;
if (Crypto.CheckMemoryHashTable(reader.ReadBytes(Files[i].HashedRegionSize), Files[i].Hash))
{
Files[i].HashValidity = Validity.Valid;
}
else
{
Files[i].HashValidity = Validity.Invalid;
}
Files[i].HashValidity = Crypto.CheckMemoryHashTable(reader.ReadBytes(Files[i].HashedRegionSize), Files[i].Hash, 0, Files[i].HashedRegionSize);
}
}

View file

@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace LibHac.Savefile
{
@ -85,19 +84,10 @@ namespace LibHac.Savefile
MetaMapEntries[i] = new MapEntry(reader);
}
HeaderHashValidity = ValidateHeaderHash();
HeaderHashValidity = Crypto.CheckMemoryHashTable(Data, Layout.Hash, 0x300, 0x3d00);
SignatureValidity = ValidateSignature(keyset);
}
private Validity ValidateHeaderHash()
{
using (SHA256 sha256 = SHA256.Create())
{
byte[] hash = sha256.ComputeHash(Data, 0x300, 0x3d00);
return Util.ArraysEqual(hash, Layout.Hash) ? Validity.Valid : Validity.Invalid;
}
}
private Validity ValidateSignature(Keyset keyset)
{
var calculatedCmac = new byte[0x10];

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace LibHac
@ -424,8 +423,8 @@ namespace LibHac
{
return false;
}
// Linq extension method is based on IEnumerable, must evaluate every item.
return first.SequenceEqual(second);
return Util.ArraysEqual(first, second);
}
public override int GetHashCode(byte[] obj)

View file

@ -134,14 +134,7 @@ namespace LibHac
}
reader.BaseStream.Position = PartitionFsHeaderAddress;
if (Crypto.CheckMemoryHashTable(reader.ReadBytes((int)PartitionFsHeaderSize), PartitionFsHeaderHash)) {
PartitionFsHeaderValidity = Validity.Valid;
}
else
{
PartitionFsHeaderValidity = Validity.Invalid;
}
PartitionFsHeaderValidity = Crypto.CheckMemoryHashTable(reader.ReadBytes((int)PartitionFsHeaderSize), PartitionFsHeaderHash, 0, (int)PartitionFsHeaderSize);
}

View file

@ -42,6 +42,7 @@ namespace hactoolnet
{
Nca,
Pfs0,
Nsp,
Romfs,
Nax0,
Xci,

View file

@ -79,7 +79,7 @@ namespace hactoolnet
if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null)
{
NcaSection section = nca.Sections.FirstOrDefault(x => x.IsExefs);
NcaSection section = nca.Sections.FirstOrDefault(x => x?.IsExefs == true);
if (section == null)
{

View file

@ -1,11 +1,54 @@
using System.IO;
using System.Reflection;
using System.Text;
using LibHac;
using static hactoolnet.Print;
namespace hactoolnet
{
internal static class ProcessNsp
{
public static void Process(Context ctx)
{
using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.Read))
{
Pfs pfs = new Pfs(file);
ctx.Logger.LogMessage(pfs.Print());
if (ctx.Options.OutDir != null)
{
pfs.Extract(ctx.Options.OutDir, ctx.Logger);
}
}
}
private static string Print(this Pfs pfs)
{
const int colLen = 36;
const int fileNameLen = 39;
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("PFS0:");
PrintItem(sb, colLen, "Magic:", pfs.Header.Magic);
PrintItem(sb, colLen, "Number of files:", pfs.Header.NumFiles);
for (int i = 0; i < pfs.Files.Length; i++)
{
PfsFileEntry file = pfs.Files[i];
string label = i == 0 ? "Files:" : "";
string offsets = $"{file.Offset:x12}-{file.Offset + file.Size:x12}{file.HashValidity.GetValidityString()}";
string data = $"pfs0:/{file.Name}".PadRight(fileNameLen) + offsets;
PrintItem(sb, colLen, label, data);
}
return sb.ToString();
}
public static void CreateNsp(Context ctx, SwitchFs switchFs)
{
ulong id = ctx.Options.TitleId;

View file

@ -31,6 +31,8 @@ namespace hactoolnet
ProcessNca.Process(ctx);
break;
case FileType.Pfs0:
case FileType.Nsp:
ProcessNsp.Process(ctx);
break;
case FileType.Romfs:
ProcessRomfs.Process(ctx);