mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add Partition FS extraction (#12)
Add Partition FS extraction + misc. changes
This commit is contained in:
parent
e798a8dd45
commit
d12f419d8d
11 changed files with 94 additions and 86 deletions
|
@ -11,12 +11,14 @@ namespace LibHac
|
||||||
internal const int Aes128Size = 0x10;
|
internal const int Aes128Size = 0x10;
|
||||||
internal const int Sha256DigestSize = 0x20;
|
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())
|
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)
|
public static void DecryptEcb(byte[] key, byte[] src, int srcIndex, byte[] dest, int destIndex, int length)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
|
||||||
using LibHac.Streams;
|
using LibHac.Streams;
|
||||||
using LibHac.XTSSharp;
|
using LibHac.XTSSharp;
|
||||||
|
|
||||||
|
@ -192,7 +191,7 @@ namespace LibHac
|
||||||
|
|
||||||
var reader = new BinaryReader(new MemoryStream(headerBytes));
|
var reader = new BinaryReader(new MemoryStream(headerBytes));
|
||||||
|
|
||||||
Header = NcaHeader.Read(reader);
|
Header = new NcaHeader(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DecryptKeyArea(Keyset keyset)
|
private void DecryptKeyArea(Keyset keyset)
|
||||||
|
@ -277,7 +276,6 @@ namespace LibHac
|
||||||
NcaSection sect = Sections[index];
|
NcaSection sect = Sections[index];
|
||||||
|
|
||||||
byte[] expected = null;
|
byte[] expected = null;
|
||||||
byte[] actual;
|
|
||||||
long offset = 0;
|
long offset = 0;
|
||||||
long size = 0;
|
long size = 0;
|
||||||
|
|
||||||
|
@ -310,15 +308,8 @@ namespace LibHac
|
||||||
stream.Position = offset;
|
stream.Position = offset;
|
||||||
stream.Read(hashTable, 0, hashTable.Length);
|
stream.Read(hashTable, 0, hashTable.Length);
|
||||||
|
|
||||||
using (SHA256 hash = SHA256.Create())
|
sect.SuperblockHashValidity = Crypto.CheckMemoryHashTable(hashTable, expected, 0, hashTable.Length);
|
||||||
{
|
if (sect.Type == SectionType.Romfs) sect.Romfs.IvfcLevels[0].HashValidity = sect.SuperblockHashValidity;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void VerifySection(int index, IProgressReport logger = null)
|
public void VerifySection(int index, IProgressReport logger = null)
|
||||||
|
@ -362,8 +353,7 @@ namespace LibHac
|
||||||
var table = new byte[level.HashSize];
|
var table = new byte[level.HashSize];
|
||||||
section.Position = level.HashOffset;
|
section.Position = level.HashOffset;
|
||||||
section.Read(table, 0, table.Length);
|
section.Read(table, 0, table.Length);
|
||||||
level.HashValidity =
|
level.HashValidity = VerifyHashTable(section, table, level.DataOffset, level.DataSize, level.HashBlockSize, true, logger);
|
||||||
VerifyHashTable(section, table, level.DataOffset, level.DataSize, level.HashBlockSize, true, logger);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,26 +367,23 @@ namespace LibHac
|
||||||
section.Position = dataOffset;
|
section.Position = dataOffset;
|
||||||
logger?.SetTotal(blockCount);
|
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;
|
Array.Clear(currentBlock, 0, currentBlock.Length);
|
||||||
if (remaining < blockSize)
|
if (!isFinalBlockFull) curBlockSize = (int)remaining;
|
||||||
{
|
|
||||||
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.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;
|
return Validity.Valid;
|
||||||
|
|
|
@ -25,50 +25,47 @@ namespace LibHac
|
||||||
|
|
||||||
public NcaFsHeader[] FsHeaders = new NcaFsHeader[4];
|
public NcaFsHeader[] FsHeaders = new NcaFsHeader[4];
|
||||||
|
|
||||||
public static NcaHeader Read(BinaryReader reader)
|
public NcaHeader(BinaryReader reader)
|
||||||
{
|
{
|
||||||
var head = new NcaHeader();
|
Signature1 = reader.ReadBytes(0x100);
|
||||||
|
Signature2 = reader.ReadBytes(0x100);
|
||||||
head.Signature1 = reader.ReadBytes(0x100);
|
Magic = reader.ReadAscii(4);
|
||||||
head.Signature2 = reader.ReadBytes(0x100);
|
if (Magic != "NCA3") throw new InvalidDataException("Not an NCA3 file");
|
||||||
head.Magic = reader.ReadAscii(4);
|
Distribution = (DistributionType)reader.ReadByte();
|
||||||
if (head.Magic != "NCA3") throw new InvalidDataException("Not an NCA3 file");
|
ContentType = (ContentType)reader.ReadByte();
|
||||||
head.Distribution = (DistributionType)reader.ReadByte();
|
CryptoType = reader.ReadByte();
|
||||||
head.ContentType = (ContentType)reader.ReadByte();
|
KaekInd = reader.ReadByte();
|
||||||
head.CryptoType = reader.ReadByte();
|
NcaSize = reader.ReadUInt64();
|
||||||
head.KaekInd = reader.ReadByte();
|
TitleId = reader.ReadUInt64();
|
||||||
head.NcaSize = reader.ReadUInt64();
|
|
||||||
head.TitleId = reader.ReadUInt64();
|
|
||||||
reader.BaseStream.Position += 4;
|
reader.BaseStream.Position += 4;
|
||||||
|
|
||||||
head.SdkVersion = new TitleVersion(reader.ReadUInt32());
|
SdkVersion = new TitleVersion(reader.ReadUInt32());
|
||||||
head.CryptoType2 = reader.ReadByte();
|
CryptoType2 = reader.ReadByte();
|
||||||
reader.BaseStream.Position += 0xF;
|
reader.BaseStream.Position += 0xF;
|
||||||
|
|
||||||
head.RightsId = reader.ReadBytes(0x10);
|
RightsId = reader.ReadBytes(0x10);
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
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++)
|
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++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
head.EncryptedKeys[i] = reader.ReadBytes(0x10);
|
EncryptedKeys[i] = reader.ReadBytes(0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.BaseStream.Position += 0xC0;
|
reader.BaseStream.Position += 0xC0;
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
head.FsHeaders[i] = new NcaFsHeader(reader);
|
FsHeaders[i] = new NcaFsHeader(reader);
|
||||||
}
|
}
|
||||||
return head;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,18 +137,12 @@ namespace LibHac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (Type == PfsType.Hfs0) {
|
if (Type == PfsType.Hfs0)
|
||||||
|
{
|
||||||
for (int i = 0; i < NumFiles; i++)
|
for (int i = 0; i < NumFiles; i++)
|
||||||
{
|
{
|
||||||
reader.BaseStream.Position = HeaderSize + Files[i].Offset;
|
reader.BaseStream.Position = HeaderSize + Files[i].Offset;
|
||||||
if (Crypto.CheckMemoryHashTable(reader.ReadBytes(Files[i].HashedRegionSize), Files[i].Hash))
|
Files[i].HashValidity = Crypto.CheckMemoryHashTable(reader.ReadBytes(Files[i].HashedRegionSize), Files[i].Hash, 0, Files[i].HashedRegionSize);
|
||||||
{
|
|
||||||
Files[i].HashValidity = Validity.Valid;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Files[i].HashValidity = Validity.Invalid;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
namespace LibHac.Savefile
|
namespace LibHac.Savefile
|
||||||
{
|
{
|
||||||
|
@ -85,19 +84,10 @@ namespace LibHac.Savefile
|
||||||
MetaMapEntries[i] = new MapEntry(reader);
|
MetaMapEntries[i] = new MapEntry(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderHashValidity = ValidateHeaderHash();
|
HeaderHashValidity = Crypto.CheckMemoryHashTable(Data, Layout.Hash, 0x300, 0x3d00);
|
||||||
SignatureValidity = ValidateSignature(keyset);
|
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)
|
private Validity ValidateSignature(Keyset keyset)
|
||||||
{
|
{
|
||||||
var calculatedCmac = new byte[0x10];
|
var calculatedCmac = new byte[0x10];
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac
|
||||||
|
@ -424,8 +423,8 @@ namespace LibHac
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public override int GetHashCode(byte[] obj)
|
||||||
|
|
|
@ -134,14 +134,7 @@ namespace LibHac
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.BaseStream.Position = PartitionFsHeaderAddress;
|
reader.BaseStream.Position = PartitionFsHeaderAddress;
|
||||||
|
PartitionFsHeaderValidity = Crypto.CheckMemoryHashTable(reader.ReadBytes((int)PartitionFsHeaderSize), PartitionFsHeaderHash, 0, (int)PartitionFsHeaderSize);
|
||||||
if (Crypto.CheckMemoryHashTable(reader.ReadBytes((int)PartitionFsHeaderSize), PartitionFsHeaderHash)) {
|
|
||||||
PartitionFsHeaderValidity = Validity.Valid;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PartitionFsHeaderValidity = Validity.Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace hactoolnet
|
||||||
{
|
{
|
||||||
Nca,
|
Nca,
|
||||||
Pfs0,
|
Pfs0,
|
||||||
|
Nsp,
|
||||||
Romfs,
|
Romfs,
|
||||||
Nax0,
|
Nax0,
|
||||||
Xci,
|
Xci,
|
||||||
|
|
|
@ -79,7 +79,7 @@ namespace hactoolnet
|
||||||
|
|
||||||
if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null)
|
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)
|
if (section == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,11 +1,54 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
using LibHac;
|
using LibHac;
|
||||||
|
using static hactoolnet.Print;
|
||||||
|
|
||||||
namespace hactoolnet
|
namespace hactoolnet
|
||||||
{
|
{
|
||||||
internal static class ProcessNsp
|
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)
|
public static void CreateNsp(Context ctx, SwitchFs switchFs)
|
||||||
{
|
{
|
||||||
ulong id = ctx.Options.TitleId;
|
ulong id = ctx.Options.TitleId;
|
||||||
|
|
|
@ -31,6 +31,8 @@ namespace hactoolnet
|
||||||
ProcessNca.Process(ctx);
|
ProcessNca.Process(ctx);
|
||||||
break;
|
break;
|
||||||
case FileType.Pfs0:
|
case FileType.Pfs0:
|
||||||
|
case FileType.Nsp:
|
||||||
|
ProcessNsp.Process(ctx);
|
||||||
break;
|
break;
|
||||||
case FileType.Romfs:
|
case FileType.Romfs:
|
||||||
ProcessRomfs.Process(ctx);
|
ProcessRomfs.Process(ctx);
|
||||||
|
|
Loading…
Reference in a new issue