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 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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace hactoolnet
|
|||
{
|
||||
Nca,
|
||||
Pfs0,
|
||||
Nsp,
|
||||
Romfs,
|
||||
Nax0,
|
||||
Xci,
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue