mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Apply section CTR and do a pfs0 superblock validity check
This commit is contained in:
parent
d336c2e58d
commit
95ee755774
5 changed files with 108 additions and 25 deletions
|
@ -9,8 +9,8 @@ namespace hactoolnet
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
ReadNca();
|
//ReadNca();
|
||||||
//ListSdfs(args);
|
ListSdfs(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ListSdfs(string[] args)
|
private static void ListSdfs(string[] args)
|
||||||
|
@ -24,16 +24,16 @@ namespace hactoolnet
|
||||||
ListTitles(sdfs);
|
ListTitles(sdfs);
|
||||||
|
|
||||||
//DecryptNax0(sdfs, "C0628FB07A89E9050BDA258F74868E8D");
|
//DecryptNax0(sdfs, "C0628FB07A89E9050BDA258F74868E8D");
|
||||||
DecryptTitle(sdfs, 0x010025400AECE000);
|
//DecryptTitle(sdfs, 0x010023900AEE0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReadNca()
|
static void ReadNca()
|
||||||
{
|
{
|
||||||
var keyset = ExternalKeys.ReadKeyFile("keys.txt", "titlekeys.txt");
|
var keyset = ExternalKeys.ReadKeyFile("keys.txt", "titlekeys.txt");
|
||||||
using (var file = new FileStream("acc9da939a8e0b339aa3be3d409d9ada.nca", FileMode.Open, FileAccess.Read))
|
using (var file = new FileStream("bf8b106a2e68df3c3f1694636423585a.nca", FileMode.Open, FileAccess.Read))
|
||||||
{
|
{
|
||||||
var nca = new Nca(keyset, file, false);
|
var nca = new Nca(keyset, file, false);
|
||||||
var romfs = nca.OpenSection(0);
|
var romfs = nca.OpenSection(0, false);
|
||||||
|
|
||||||
using (var output = new FileStream("romfs_net", FileMode.Create))
|
using (var output = new FileStream("romfs_net", FileMode.Create))
|
||||||
{
|
{
|
||||||
|
@ -77,7 +77,7 @@ namespace hactoolnet
|
||||||
|
|
||||||
static SdFs LoadSdFs(string[] args)
|
static SdFs LoadSdFs(string[] args)
|
||||||
{
|
{
|
||||||
var keyset = ExternalKeys.ReadKeyFile(args[0]);
|
var keyset = ExternalKeys.ReadKeyFile(args[0], "titlekeys.txt");
|
||||||
keyset.SetSdSeed(args[1].ToBytes());
|
keyset.SetSdSeed(args[1].ToBytes());
|
||||||
var sdfs = new SdFs(keyset, args[2]);
|
var sdfs = new SdFs(keyset, args[2]);
|
||||||
return sdfs;
|
return sdfs;
|
||||||
|
@ -103,6 +103,16 @@ namespace hactoolnet
|
||||||
$" {content.NcaId.ToHexString()}.nca {content.Type} {Util.GetBytesReadable(content.Size)}");
|
$" {content.NcaId.ToHexString()}.nca {content.Type} {Util.GetBytesReadable(content.Size)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var nca in title.Ncas)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" {nca.HasRightsId} {nca.NcaId} {nca.Header.ContentType}");
|
||||||
|
|
||||||
|
foreach (var sect in nca.Sections)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" {sect.SectionNum} {sect.Type} {sect.Header.CryptType} {sect.SuperblockHashValidity}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine("");
|
Console.WriteLine("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using libhac.XTSSharp;
|
using libhac.XTSSharp;
|
||||||
|
@ -40,6 +41,7 @@ namespace libhac
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int DefaultSectorSize = 16;
|
public const int DefaultSectorSize = 16;
|
||||||
|
|
||||||
|
private readonly byte[] _initialCounter;
|
||||||
private readonly long _counterOffset;
|
private readonly long _counterOffset;
|
||||||
private readonly byte[] _tempBuffer;
|
private readonly byte[] _tempBuffer;
|
||||||
private readonly Aes _aes;
|
private readonly Aes _aes;
|
||||||
|
@ -62,9 +64,16 @@ namespace libhac
|
||||||
/// <param name="offset">Offset to start at in the input stream</param>
|
/// <param name="offset">Offset to start at in the input stream</param>
|
||||||
/// <param name="length">The length of the created stream</param>
|
/// <param name="length">The length of the created stream</param>
|
||||||
/// <param name="counterOffset">Offset to add to the counter</param>
|
/// <param name="counterOffset">Offset to add to the counter</param>
|
||||||
public AesCtrStream(Stream baseStream, byte[] key, long offset, long length, long counterOffset)
|
/// <param name="ctrHi">The value of the upper 64 bits of the counter</param>
|
||||||
|
public AesCtrStream(Stream baseStream, byte[] key, long offset, long length, long counterOffset, byte[] ctrHi = null)
|
||||||
: base(baseStream, 0x10, offset)
|
: base(baseStream, 0x10, offset)
|
||||||
{
|
{
|
||||||
|
_initialCounter = new byte[0x10];
|
||||||
|
if (ctrHi != null)
|
||||||
|
{
|
||||||
|
Array.Copy(ctrHi, _initialCounter, 8);
|
||||||
|
}
|
||||||
|
|
||||||
_counterOffset = counterOffset;
|
_counterOffset = counterOffset;
|
||||||
Length = length;
|
Length = length;
|
||||||
_tempBuffer = new byte[0x10];
|
_tempBuffer = new byte[0x10];
|
||||||
|
@ -80,7 +89,7 @@ namespace libhac
|
||||||
|
|
||||||
private CounterModeCryptoTransform CreateDecryptor()
|
private CounterModeCryptoTransform CreateDecryptor()
|
||||||
{
|
{
|
||||||
var dec = new CounterModeCryptoTransform(_aes, _aes.Key, new byte[0x10]);
|
var dec = new CounterModeCryptoTransform(_aes, _aes.Key, _initialCounter ?? new byte[0x10]);
|
||||||
dec.UpdateCounter(_counterOffset + Position);
|
dec.UpdateCounter(_counterOffset + Position);
|
||||||
return dec;
|
return dec;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using libhac.XTSSharp;
|
using libhac.XTSSharp;
|
||||||
|
|
||||||
namespace libhac
|
namespace libhac
|
||||||
|
@ -34,15 +35,24 @@ namespace libhac
|
||||||
{
|
{
|
||||||
DecryptKeyArea(keyset);
|
DecryptKeyArea(keyset);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (keyset.TitleKeys.TryGetValue(Header.RightsId, out var titleKey))
|
||||||
|
{
|
||||||
|
Crypto.DecryptEcb(keyset.titlekeks[CryptoType], titleKey, DecryptedKeys[2], 0x10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
var section = ParseSection(i);
|
var section = ParseSection(i);
|
||||||
if (section != null) Sections.Add(section);
|
if (section == null) continue;
|
||||||
|
Sections.Add(section);
|
||||||
|
ValidateSuperblockHash(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream OpenSection(int index)
|
public Stream OpenSection(int index, bool raw)
|
||||||
{
|
{
|
||||||
if (index >= Sections.Count) throw new ArgumentOutOfRangeException(nameof(index));
|
if (index >= Sections.Count) throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
var sect = Sections[index];
|
var sect = Sections[index];
|
||||||
|
@ -50,18 +60,22 @@ namespace libhac
|
||||||
long offset = sect.Offset;
|
long offset = sect.Offset;
|
||||||
long size = sect.Size;
|
long size = sect.Size;
|
||||||
|
|
||||||
switch (sect.Header.FsType)
|
if (!raw)
|
||||||
{
|
{
|
||||||
case SectionFsType.Pfs0:
|
switch (sect.Header.FsType)
|
||||||
offset = sect.Offset + sect.Pfs0.Pfs0Offset;
|
{
|
||||||
size = sect.Pfs0.Pfs0Size;
|
case SectionFsType.Pfs0:
|
||||||
break;
|
offset = sect.Offset + sect.Pfs0.Pfs0Offset;
|
||||||
case SectionFsType.Romfs:
|
size = sect.Pfs0.Pfs0Size;
|
||||||
offset = sect.Offset + (long)sect.Header.Romfs.IvfcHeader.LevelHeaders[Romfs.IvfcMaxLevel - 1].LogicalOffset;
|
break;
|
||||||
size = (long)sect.Header.Romfs.IvfcHeader.LevelHeaders[Romfs.IvfcMaxLevel - 1].HashDataSize;
|
case SectionFsType.Romfs:
|
||||||
break;
|
offset = sect.Offset + (long)sect.Header.Romfs.IvfcHeader.LevelHeaders[Romfs.IvfcMaxLevel - 1]
|
||||||
default:
|
.LogicalOffset;
|
||||||
throw new ArgumentOutOfRangeException();
|
size = (long)sect.Header.Romfs.IvfcHeader.LevelHeaders[Romfs.IvfcMaxLevel - 1].HashDataSize;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream.Position = offset;
|
Stream.Position = offset;
|
||||||
|
@ -73,7 +87,7 @@ namespace libhac
|
||||||
case SectionCryptType.XTS:
|
case SectionCryptType.XTS:
|
||||||
break;
|
break;
|
||||||
case SectionCryptType.CTR:
|
case SectionCryptType.CTR:
|
||||||
return new RandomAccessSectorStream(new AesCtrStream(Stream, DecryptedKeys[2], offset, size, offset), false);
|
return new RandomAccessSectorStream(new AesCtrStream(Stream, DecryptedKeys[2], offset, size, offset, sect.Header.Ctr), false);
|
||||||
case SectionCryptType.BKTR:
|
case SectionCryptType.BKTR:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -128,6 +142,47 @@ namespace libhac
|
||||||
return sect;
|
return sect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ValidateSuperblockHash(int index)
|
||||||
|
{
|
||||||
|
if (index >= Sections.Count) throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
var sect = Sections[index];
|
||||||
|
var stream = OpenSection(index, true);
|
||||||
|
|
||||||
|
byte[] expected = null;
|
||||||
|
byte[] actual;
|
||||||
|
long offset = 0;
|
||||||
|
long size = 0;
|
||||||
|
|
||||||
|
switch (sect.Type)
|
||||||
|
{
|
||||||
|
case SectionType.Invalid:
|
||||||
|
break;
|
||||||
|
case SectionType.Pfs0:
|
||||||
|
var pfs0 = sect.Header.Pfs0;
|
||||||
|
expected = pfs0.MasterHash;
|
||||||
|
offset = pfs0.HashTableOffset;
|
||||||
|
size = pfs0.HashTableSize;
|
||||||
|
break;
|
||||||
|
case SectionType.Romfs:
|
||||||
|
break;
|
||||||
|
case SectionType.Bktr:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected == null) return;
|
||||||
|
|
||||||
|
var hashTable = new byte[size];
|
||||||
|
stream.Position = offset;
|
||||||
|
stream.Read(hashTable, 0, hashTable.Length);
|
||||||
|
|
||||||
|
using (SHA256 hash = SHA256.Create())
|
||||||
|
{
|
||||||
|
actual = hash.ComputeHash(hashTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
sect.SuperblockHashValidity = Util.ArraysEqual(expected, actual) ? Validity.Valid : Validity.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (!KeepOpen)
|
if (!KeepOpen)
|
||||||
|
@ -145,6 +200,7 @@ namespace libhac
|
||||||
public int SectionNum { get; set; }
|
public int SectionNum { get; set; }
|
||||||
public long Offset { get; set; }
|
public long Offset { get; set; }
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
|
public Validity SuperblockHashValidity { get; set; }
|
||||||
|
|
||||||
public Pfs0Superblock Pfs0 { get; set; }
|
public Pfs0Superblock Pfs0 { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace libhac
|
namespace libhac
|
||||||
{
|
{
|
||||||
|
@ -96,7 +97,7 @@ namespace libhac
|
||||||
public Pfs0Superblock Pfs0;
|
public Pfs0Superblock Pfs0;
|
||||||
public RomfsSuperblock Romfs;
|
public RomfsSuperblock Romfs;
|
||||||
public BktrSuperblock Bktr;
|
public BktrSuperblock Bktr;
|
||||||
public ulong Ctr;
|
public byte[] Ctr;
|
||||||
|
|
||||||
public NcaFsHeader(BinaryReader reader)
|
public NcaFsHeader(BinaryReader reader)
|
||||||
{
|
{
|
||||||
|
@ -126,7 +127,7 @@ namespace libhac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ctr = reader.ReadUInt64();
|
Ctr = reader.ReadBytes(8).Reverse().ToArray();
|
||||||
reader.BaseStream.Position += 184;
|
reader.BaseStream.Position += 184;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,4 +281,11 @@ namespace libhac
|
||||||
Romfs,
|
Romfs,
|
||||||
Bktr
|
Bktr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Validity
|
||||||
|
{
|
||||||
|
Unchecked,
|
||||||
|
Invalid,
|
||||||
|
Valid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ namespace libhac
|
||||||
var title = new Title();
|
var title = new Title();
|
||||||
|
|
||||||
// Meta contents always have 1 Partition FS section with 1 file in it
|
// Meta contents always have 1 Partition FS section with 1 file in it
|
||||||
Stream sect = nca.OpenSection(0);
|
Stream sect = nca.OpenSection(0, false);
|
||||||
var pfs0 = new Pfs0(sect);
|
var pfs0 = new Pfs0(sect);
|
||||||
var file = pfs0.GetFile(0);
|
var file = pfs0.GetFile(0);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue