Small enhancements in old code + some fixes for AesCtrBufferedInputStream
continuous-integration/drone/push Build is passing Details

master
Dmitry Isaenko 2022-09-11 05:14:37 +03:00
parent 0cc5d41aef
commit 9b5eacdef9
14 changed files with 219 additions and 86 deletions

View File

@ -90,7 +90,7 @@ public class NCAContent {
} }
private void proceedPFS0NotEncrypted() throws Exception{ private void proceedPFS0NotEncrypted() throws Exception{
RandomAccessFile raf = new RandomAccessFile(file, "r"); RandomAccessFile raf = new RandomAccessFile(file, "r");
long thisMediaLocation = offsetPosition + (ncaHeaderTableEntry.getMediaStartOffset() * 0x200); long thisMediaLocation = offsetPosition + (ncaHeaderTableEntry.getMediaStartOffset() * 0x200);// TODO: NOTE already defined inside PFS0
long hashTableLocation = thisMediaLocation + ncaFsHeader.getSuperBlockPFS0().getHashTableOffset(); long hashTableLocation = thisMediaLocation + ncaFsHeader.getSuperBlockPFS0().getHashTableOffset();
long pfs0Location = thisMediaLocation + ncaFsHeader.getSuperBlockPFS0().getPfs0offset(); long pfs0Location = thisMediaLocation + ncaFsHeader.getSuperBlockPFS0().getPfs0offset();
@ -112,13 +112,37 @@ public class NCAContent {
// Get pfs0 // Get pfs0
pfs0 = new PFS0Provider(file, pfs0Location); pfs0 = new PFS0Provider(file, pfs0Location);
} }
private void proceedPFS0Encrypted() throws Exception{ private void proceedPFS0Encrypted() throws Exception{/*
RandomAccessFile raf = new RandomAccessFile(file, "r");
long thisMediaLocation = offsetPosition + (ncaHeaderTableEntry.getMediaStartOffset() * 0x200);
long hashTableLocation = thisMediaLocation + ncaFsHeader.getSuperBlockPFS0().getHashTableOffset();
long pfs0Location = thisMediaLocation + ncaFsHeader.getSuperBlockPFS0().getPfs0offset();
raf.seek(hashTableLocation);
byte[] rawData;
long sha256recordsNumber = ncaFsHeader.getSuperBlockPFS0().getHashTableSize() / 0x20;
// Collect hashes
for (int i = 0; i < sha256recordsNumber; i++){
rawData = new byte[0x20]; // 32 bytes - size of SHA256 hash
if (raf.read(rawData) != -1)
Pfs0SHA256hashes.add(rawData);
else {
raf.close();
return; // TODO: fix
}
}
raf.close();
// Get pfs0
pfs0 = new PFS0Provider(file, pfs0Location);
/*/
new CryptoSection03Pfs0(file, new CryptoSection03Pfs0(file,
offsetPosition, offsetPosition,
decryptedKey, decryptedKey,
ncaFsHeader, ncaFsHeader,
ncaHeaderTableEntry.getMediaStartOffset(), ncaHeaderTableEntry.getMediaStartOffset(),
ncaHeaderTableEntry.getMediaEndOffset()); ncaHeaderTableEntry.getMediaEndOffset());
//*/
} }
private void proceedRomFs() throws Exception{ private void proceedRomFs() throws Exception{
@ -170,20 +194,22 @@ public class NCAContent {
"SHA256 hash table size: " + RainbowDump.formatDecHexString(ncaFsHeader.getSuperBlockPFS0().getHashTableSize()) + "\n" + "SHA256 hash table size: " + RainbowDump.formatDecHexString(ncaFsHeader.getSuperBlockPFS0().getHashTableSize()) + "\n" +
"SHA256 hash table offs: " + RainbowDump.formatDecHexString(ncaFsHeader.getSuperBlockPFS0().getHashTableOffset()) + "\n" + "SHA256 hash table offs: " + RainbowDump.formatDecHexString(ncaFsHeader.getSuperBlockPFS0().getHashTableOffset()) + "\n" +
"PFS0 Offset: " + RainbowDump.formatDecHexString(ncaFsHeader.getSuperBlockPFS0().getPfs0offset()) + "\n" + "PFS0 Offset: " + RainbowDump.formatDecHexString(ncaFsHeader.getSuperBlockPFS0().getPfs0offset()) + "\n" +
"SHA256 records: " + RainbowDump.formatDecHexString((ncaFsHeader.getSuperBlockPFS0().getHashTableSize() / 0x20)) + "\n" + "SHA256 records: " + RainbowDump.formatDecHexString(ncaFsHeader.getSuperBlockPFS0().getHashTableSize() / 0x20) + "\n" +
"KEY (decrypted): " + Converter.byteArrToHexString(decryptedKey) + "\n" + "KEY (decrypted): " + Converter.byteArrToHexString(decryptedKey) + "\n" +
"CTR: " + Converter.byteArrToHexString(ncaFsHeader.getSectionCTR()) + "\n"); "CTR: " + Converter.byteArrToHexString(ncaFsHeader.getSectionCTR()) + "\n" +
"-----------------------------------------------------------\n");
if (decryptedKey == null) if (decryptedKey == null)
throw new Exception("CryptoSection03: unable to proceed. No decrypted key provided."); throw new Exception("CryptoSection03: unable to proceed. No decrypted key provided.");
RandomAccessFile raf = new RandomAccessFile(file, "r"); RandomAccessFile raf = new RandomAccessFile(file, "r");
long abosluteOffsetPosition = offsetPosition + (mediaStartBlocksOffset * 0x200); long absoluteOffsetPosition = offsetPosition + (mediaStartBlocksOffset * 0x200);
raf.seek(abosluteOffsetPosition); raf.seek(absoluteOffsetPosition);
AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey, ncaFsHeader.getSectionCTR(), mediaStartBlocksOffset * 0x200); AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey, ncaFsHeader.getSectionCTR(),
mediaStartBlocksOffset * 0x200);
byte[] encryptedBlock; byte[] encryptedBlock;
byte[] dectyptedBlock; byte[] decryptedBlock;
long mediaBlocksSize = mediaEndBlocksOffset - mediaStartBlocksOffset; long mediaBlocksSize = mediaEndBlocksOffset - mediaStartBlocksOffset;
// Prepare thread to parse encrypted data // Prepare thread to parse encrypted data
PipedOutputStream streamOut = new PipedOutputStream(); PipedOutputStream streamOut = new PipedOutputStream();
@ -206,11 +232,11 @@ public class NCAContent {
for (int i = 0; i < mediaBlocksSize; i++){ for (int i = 0; i < mediaBlocksSize; i++){
encryptedBlock = new byte[0x200]; encryptedBlock = new byte[0x200];
if (raf.read(encryptedBlock) != -1){ if (raf.read(encryptedBlock) != -1){
//dectyptedBlock = aesCtr.decrypt(encryptedBlock); //decryptedBlock = aesCtr.decrypt(encryptedBlock);
dectyptedBlock = decryptor.decryptNext(encryptedBlock); decryptedBlock = decryptor.decryptNext(encryptedBlock);
// Writing decrypted data to pipe // Writing decrypted data to pipe
try { try {
streamOut.write(dectyptedBlock); streamOut.write(decryptedBlock);
} }
catch (IOException e){ catch (IOException e){
break; break;

View File

@ -310,7 +310,6 @@ public class NCAProvider {
} }
catch (EmptySectionException ignored){} catch (EmptySectionException ignored){}
catch (Exception e){ catch (Exception e){
this.ncaContent3 = null;
log.debug("Unable to get NCA Content "+number, e); log.debug("Unable to get NCA Content "+number, e);
} }
} }

View File

@ -28,4 +28,6 @@ public interface IPFS0Provider extends ISuperProvider {
byte[] getPadding(); byte[] getPadding();
PFS0subFile[] getPfs0subFiles(); PFS0subFile[] getPfs0subFiles();
void printDebug();
} }

View File

@ -18,16 +18,24 @@
*/ */
package libKonogonka.Tools.PFS0; package libKonogonka.Tools.PFS0;
import libKonogonka.Converter;
import libKonogonka.RainbowDump;
import libKonogonka.Tools.RomFs.Level6Header;
import libKonogonka.ctraes.AesCtrDecryptSimple; import libKonogonka.ctraes.AesCtrDecryptSimple;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Arrays; import java.util.Arrays;
import static libKonogonka.Converter.*; import static libKonogonka.Converter.*;
public class PFS0EncryptedProvider implements IPFS0Provider{ public class PFS0EncryptedProvider implements IPFS0Provider{
private long rawFileDataStart; // Always -1 @ PFS0EncryptedProvider private final static Logger log = LogManager.getLogger(PFS0EncryptedProvider.class);
//private long rawFileDataStart; // Always -1 @ PFS0EncryptedProvider
private final String magic; private final String magic;
private final int filesCount; private final int filesCount;
@ -56,14 +64,14 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
long mediaEndOffset long mediaEndOffset
) throws Exception{ ) throws Exception{
// Populate 'meta' data that is needed for getProviderSubFilePipedInpStream() // Populate 'meta' data that is needed for getProviderSubFilePipedInpStream()
this.offsetPositionInFile = offsetPositionInFile; this.offsetPositionInFile = offsetPositionInFile + mediaStartOffset*0x200;
this.file = fileWithEncPFS0; this.file = fileWithEncPFS0;
this.key = key; this.key = key;
this.sectionCTR = sectionCTR; this.sectionCTR = sectionCTR;
this.mediaStartOffset = mediaStartOffset; this.mediaStartOffset = mediaStartOffset;
this.mediaEndOffset = mediaEndOffset; this.mediaEndOffset = mediaEndOffset;
// pfs0offsetPosition is a position relative to Media block. Let's add pfs0 'header's' bytes count and get raw data start position in media block // pfs0offsetPosition is a position relative to Media block. Let's add pfs0 'header's' bytes count and get raw data start position in media block
rawFileDataStart = -1; // Set -1 for PFS0EncryptedProvider //rawFileDataStart = -1; // Set -1 for PFS0EncryptedProvider
// Detect raw data start position using next var // Detect raw data start position using next var
rawBlockDataStart = pfs0offsetPosition; rawBlockDataStart = pfs0offsetPosition;
@ -161,7 +169,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
@Override @Override
public byte[] getPadding() { return padding; } public byte[] getPadding() { return padding; }
@Override @Override
public long getRawFileDataStart() { return rawFileDataStart; } public long getRawFileDataStart() { return rawBlockDataStart; }
@Override @Override
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; } public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
@Override @Override
@ -176,14 +184,21 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
PipedInputStream streamIn = new PipedInputStream(streamOut); PipedInputStream streamIn = new PipedInputStream(streamOut);
workerThread = new Thread(() -> { workerThread = new Thread(() -> {
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Executing thread"); log.debug("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Executing thread:\nSub file: " +
pfs0subFiles[subFileNumber].getName() +
"\nFor block # "+((rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) / 0x200) +
"\nAnd initial skipped bytes are: "+offsetPositionInFile +
"\nWhere Raw Block Data Start: "+rawBlockDataStart +
"\nAnd sub file offset: "+pfs0subFiles[subFileNumber].getOffset()+
"\nSkip bytes "+((rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) - ((rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) / 0x200) * 0x200)+
"\nKEY "+Converter.byteArrToHexString(key)+
"\nSection CTR "+Converter.byteArrToHexString(sectionCTR)+
"\n______________________________________________________________");
try { try {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath()));
// Let's store what we're about to skip
long skipInitL = offsetPositionInFile + (mediaStartOffset * 0x200); // NOTE: NEVER cast to int.
// Check if skip was successful // Check if skip was successful
if (bis.skip(skipInitL) != skipInitL) { if (bis.skip(offsetPositionInFile) != offsetPositionInFile) {
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Failed to skip range "+skipInitL); System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Failed to skip range "+offsetPositionInFile);
return; return;
} }
@ -209,7 +224,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
//----------------------------- Step 1: get starting bytes from the end of the junk block -------------------------------- //----------------------------- Step 1: get starting bytes from the end of the junk block --------------------------------
// Since our data could be located in position with some offset from the decrypted block, let's skip bytes left. Considering the case when data is not aligned to block // Since our data could be located in position with some offset from the decrypted block, let's skip bytes left. Considering the case when data is not aligned to block
skipBytes = (int) ( (rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) - startBlock * 0x200); // <- How much bytes shall we skip to reach requested data start of sub-file skipBytes = (int) ((rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) - startBlock * 0x200); // <- How much bytes shall we skip to reach requested data start of sub-file
if (skipBytes > 0) { if (skipBytes > 0) {
encryptedBlock = new byte[0x200]; encryptedBlock = new byte[0x200];
@ -301,4 +316,28 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
} }
return null; return null;
} }
public void printDebug(){
log.debug(".:: PFS0EncryptedProvider ::.\n" +
"File name: " + file.getName() + "\n" +
"Raw block data start " + RainbowDump.formatDecHexString(rawBlockDataStart) + "\n" +
"Magic " + magic + "\n" +
"Files count " + RainbowDump.formatDecHexString(filesCount) + "\n" +
"String Table Size " + RainbowDump.formatDecHexString(stringTableSize) + "\n" +
"Padding " + Converter.byteArrToHexString(padding) + "\n\n" +
"Offset position in file " + RainbowDump.formatDecHexString(offsetPositionInFile) + "\n" +
"Media Start Offset " + RainbowDump.formatDecHexString(mediaStartOffset) + "\n" +
"Media End Offset " + RainbowDump.formatDecHexString(mediaEndOffset) + "\n"
);
for (PFS0subFile subFile : pfs0subFiles){
log.debug(
"\nName: " + subFile.getName() + "\n" +
"Offset " + RainbowDump.formatDecHexString(subFile.getOffset()) + "\n" +
"Size " + RainbowDump.formatDecHexString(subFile.getSize()) + "\n" +
"Zeroes " + Converter.byteArrToHexString(subFile.getZeroes()) + "\n" +
"----------------------------------------------------------------"
);
}
}
} }

View File

@ -18,52 +18,99 @@
*/ */
package libKonogonka.Tools.PFS0; package libKonogonka.Tools.PFS0;
import libKonogonka.Converter;
import libKonogonka.RainbowDump;
import libKonogonka.ctraes.AesCtrBufferedInputStream;
import libKonogonka.ctraes.AesCtrDecryptSimple;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Arrays; import java.util.Arrays;
import static libKonogonka.Converter.*; import static libKonogonka.Converter.*;
public class PFS0Provider implements IPFS0Provider{ public class PFS0Provider implements IPFS0Provider{
private final long rawFileDataStart; // Where data starts, excluding header, string table etc. private final static Logger log = LogManager.getLogger(PFS0Provider.class);
private final String magic; private long rawFileDataStartOffset;
private final int filesCount;
private final int stringTableSize; private String magic;
private final byte[] padding; private int filesCount;
private final PFS0subFile[] pfs0subFiles; private int stringTableSize;
private byte[] padding;
private PFS0subFile[] pfs0subFiles;
private final File file; private final File file;
private final long offsetPosition; // Where data starts, excluding header, string table etc.
private long mediaStartOffset;
private long mediaEndOffset;
private AesCtrDecryptSimple decryptor;
private final boolean encrypted;
public PFS0Provider(File fileWithPfs0,
long offsetPosition,
long mediaStartOffset,
long mediaEndOffset,
AesCtrDecryptSimple decryptor) throws Exception{
this.file = fileWithPfs0;
this.offsetPosition = offsetPosition + mediaStartOffset*0x200;
this.encrypted = true;
this.mediaStartOffset = mediaStartOffset;
this.mediaEndOffset = mediaEndOffset;
this.decryptor = decryptor;
proceedPfs0();
}
public PFS0Provider(File fileWithPfs0) throws Exception{ this(fileWithPfs0, 0); } public PFS0Provider(File fileWithPfs0) throws Exception{ this(fileWithPfs0, 0); }
public PFS0Provider(File fileWithPfs0, long pfs0offsetPosition) throws Exception{ public PFS0Provider(File fileWithPfs0, long offsetPosition) throws Exception{
file = fileWithPfs0; this.file = fileWithPfs0;
RandomAccessFile raf = new RandomAccessFile(fileWithPfs0, "r"); // TODO: replace to bufferedInputStream this.offsetPosition = offsetPosition;
this.encrypted = false;
//bufferedInputStream = new BufferedInputStream(Files.newInputStream(fileWithPfs0.toPath()));
proceedPfs0();
}
private void proceedPfs0() throws Exception{
BufferedInputStream bufferedInputStream;
if (encrypted) {
bufferedInputStream = new AesCtrBufferedInputStream(decryptor,
offsetPosition,
mediaStartOffset,
mediaEndOffset,
Files.newInputStream(file.toPath()));
}
else{
bufferedInputStream = new BufferedInputStream(Files.newInputStream(file.toPath()));
}
if (offsetPosition != bufferedInputStream.skip(offsetPosition))
throw new Exception("PFS0Provider: Unable to skip initial offset: "+offsetPosition);
raf.seek(pfs0offsetPosition);
byte[] fileStartingBytes = new byte[0x10]; byte[] fileStartingBytes = new byte[0x10];
// Read PFS0Provider, files count, header, padding (4 zero bytes) // Read PFS0Provider, files count, header, padding (4 zero bytes)
if (raf.read(fileStartingBytes) != 0x10){ if (bufferedInputStream.read(fileStartingBytes) != 0x10){
raf.close();
throw new Exception("PFS0Provider: Unable to read starting bytes"); throw new Exception("PFS0Provider: Unable to read starting bytes");
} }
rawFileDataStartOffset += 0x10;
// Check PFS0Provider // Check PFS0Provider
magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII); magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII);
if (! magic.equals("PFS0")){ if (! magic.equals("PFS0")){
raf.close();
throw new Exception("PFS0Provider: Bad magic"); throw new Exception("PFS0Provider: Bad magic");
} }
// Get files count // Get files count
filesCount = getLEint(fileStartingBytes, 0x4); filesCount = getLEint(fileStartingBytes, 0x4);
if (filesCount <= 0 ) { if (filesCount <= 0 ) {
raf.close();
throw new Exception("PFS0Provider: Files count is too small"); throw new Exception("PFS0Provider: Files count is too small");
} }
// Get string table // Get string table
stringTableSize = getLEint(fileStartingBytes, 0x8); stringTableSize = getLEint(fileStartingBytes, 0x8);
if (stringTableSize <= 0 ){ if (stringTableSize <= 0 ){
raf.close();
throw new Exception("PFS0Provider: String table is too small"); throw new Exception("PFS0Provider: String table is too small");
} }
padding = Arrays.copyOfRange(fileStartingBytes, 0xc, 0x10); padding = Arrays.copyOfRange(fileStartingBytes, 0xc, 0x10);
@ -77,21 +124,22 @@ public class PFS0Provider implements IPFS0Provider{
byte[] fileEntryTable = new byte[0x18]; byte[] fileEntryTable = new byte[0x18];
for (int i=0; i<filesCount; i++){ for (int i=0; i<filesCount; i++){
if (raf.read(fileEntryTable) != 0x18) if (bufferedInputStream.read(fileEntryTable) != 0x18)
throw new Exception("PFS0Provider: String table is too small"); throw new Exception("PFS0Provider: String table is too small");
offsetsSubFiles[i] = getLElong(fileEntryTable, 0); offsetsSubFiles[i] = getLElong(fileEntryTable, 0);
sizesSubFiles[i] = getLElong(fileEntryTable, 0x8); sizesSubFiles[i] = getLElong(fileEntryTable, 0x8);
strTableOffsets[i] = getLEint(fileEntryTable, 0x10); strTableOffsets[i] = getLEint(fileEntryTable, 0x10);
zeroBytes[i] = Arrays.copyOfRange(fileEntryTable, 0x14, 0x18); zeroBytes[i] = Arrays.copyOfRange(fileEntryTable, 0x14, 0x18);
rawFileDataStartOffset += 0x18;
} }
//********************************************************************************************************** //**********************************************************************************************************
// In here pointer in front of String table // In here pointer in front of String table
String[] subFileNames = new String[filesCount]; String[] subFileNames = new String[filesCount];
byte[] stringTbl = new byte[stringTableSize]; byte[] stringTbl = new byte[stringTableSize];
if (raf.read(stringTbl) != stringTableSize){ if (bufferedInputStream.read(stringTbl) != stringTableSize){
throw new Exception("Read PFS0Provider String table failure. Can't read requested string table size ("+stringTableSize+")"); throw new Exception("Read PFS0Provider String table failure. Can't read requested string table size ("+stringTableSize+")");
} }
rawFileDataStartOffset += stringTableSize;
for (int i=0; i < filesCount; i++){ for (int i=0; i < filesCount; i++){
int j = 0; int j = 0;
while (stringTbl[strTableOffsets[i]+j] != (byte)0x00) while (stringTbl[strTableOffsets[i]+j] != (byte)0x00)
@ -106,12 +154,11 @@ public class PFS0Provider implements IPFS0Provider{
zeroBytes[i] zeroBytes[i]
); );
} }
rawFileDataStart = raf.getFilePointer(); bufferedInputStream.close();
raf.close();
} }
@Override @Override
public boolean isEncrypted() { return false; } public boolean isEncrypted() { return encrypted; }
@Override @Override
public String getMagic() { return magic; } public String getMagic() { return magic; }
@Override @Override
@ -121,7 +168,7 @@ public class PFS0Provider implements IPFS0Provider{
@Override @Override
public byte[] getPadding() { return padding; } public byte[] getPadding() { return padding; }
@Override @Override
public long getRawFileDataStart() { return rawFileDataStart; } public long getRawFileDataStart() { return rawFileDataStartOffset; }
@Override @Override
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; } public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
@Override @Override
@ -139,7 +186,7 @@ public class PFS0Provider implements IPFS0Provider{
workerThread = new Thread(() -> { workerThread = new Thread(() -> {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Executing thread"); System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Executing thread");
try { try {
long subFileRealPosition = rawFileDataStart + pfs0subFiles[subFileNumber].getOffset(); long subFileRealPosition = rawFileDataStartOffset + pfs0subFiles[subFileNumber].getOffset();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
if (bis.skip(subFileRealPosition) != subFileRealPosition) { if (bis.skip(subFileRealPosition) != subFileRealPosition) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to skip requested offset"); System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to skip requested offset");
@ -186,4 +233,24 @@ public class PFS0Provider implements IPFS0Provider{
} }
return null; return null;
} }
public void printDebug(){
log.debug(".:: PFS0Provider ::.\n" +
"File name: " + file.getName() + "\n\n" +
"Raw file data start: " + RainbowDump.formatDecHexString(rawFileDataStartOffset) + "\n" +
"Magic " + magic + "\n" +
"Files count " + RainbowDump.formatDecHexString(filesCount) + "\n" +
"String Table Size " + RainbowDump.formatDecHexString(stringTableSize) + "\n" +
"Padding " + Converter.byteArrToHexString(padding) + "\n"
);
for (PFS0subFile subFile : pfs0subFiles){
log.debug(
"\nName: " + subFile.getName() + "\n" +
"Offset " + RainbowDump.formatDecHexString(subFile.getOffset()) + "\n" +
"Size " + RainbowDump.formatDecHexString(subFile.getSize()) + "\n" +
"Zeroes " + Converter.byteArrToHexString(subFile.getZeroes()) + "\n" +
"----------------------------------------------------------------"
);
}
}
} }

View File

@ -19,10 +19,10 @@
package libKonogonka.Tools.PFS0; package libKonogonka.Tools.PFS0;
public class PFS0subFile { public class PFS0subFile {
private String name; private final String name;
private long offset; // REAL in file (including offset in NCA/NSP file) private final long offset; // REAL in file (including offset in NCA/NSP file)
private long size; private final long size;
private byte[] zeroes; private final byte[] zeroes;
public PFS0subFile(String name, long offset, long size, byte[] zeroesInTable){ public PFS0subFile(String name, long offset, long size, byte[] zeroesInTable){
this.name = name; this.name = name;

View File

@ -41,8 +41,8 @@ public class FileSystemEntry {
private static byte[] dirsMetadataTable; private static byte[] dirsMetadataTable;
private static byte[] filesMetadataTable; private static byte[] filesMetadataTable;
private long fileOffset; private long offset;
private long fileSize; private long size;
public FileSystemEntry(byte[] dirsMetadataTable, byte[] filesMetadataTable) throws Exception{ public FileSystemEntry(byte[] dirsMetadataTable, byte[] filesMetadataTable) throws Exception{
FileSystemEntry.dirsMetadataTable = dirsMetadataTable; FileSystemEntry.dirsMetadataTable = dirsMetadataTable;
@ -62,7 +62,7 @@ public class FileSystemEntry {
content.add(getDirectory(rootDirectoryMetaData.firstSubdirectoryOffset)); content.add(getDirectory(rootDirectoryMetaData.firstSubdirectoryOffset));
if (rootDirectoryMetaData.firstFileOffset != -1) if (rootDirectoryMetaData.firstFileOffset != -1)
content.add(getFile(this, rootDirectoryMetaData.firstFileOffset)); content.add(getFile(this, rootDirectoryMetaData.firstFileOffset));
content.sort(Comparator.comparingLong(FileSystemEntry::getFileOffset)); content.sort(Comparator.comparingLong(FileSystemEntry::getOffset));
} }
private FileSystemEntry(){ private FileSystemEntry(){
@ -85,7 +85,7 @@ public class FileSystemEntry {
if (directoryMetaData.firstFileOffset != -1) if (directoryMetaData.firstFileOffset != -1)
fileSystemEntry.content.add(getFile(fileSystemEntry, directoryMetaData.firstFileOffset)); fileSystemEntry.content.add(getFile(fileSystemEntry, directoryMetaData.firstFileOffset));
fileSystemEntry.content.sort(Comparator.comparingLong(FileSystemEntry::getFileOffset)); fileSystemEntry.content.sort(Comparator.comparingLong(FileSystemEntry::getOffset));
return fileSystemEntry; return fileSystemEntry;
} }
@ -96,8 +96,8 @@ public class FileSystemEntry {
FileMetaData fileMetaData = new FileMetaData(childFileMetaPosition); FileMetaData fileMetaData = new FileMetaData(childFileMetaPosition);
fileSystemEntry.name = fileMetaData.fileName; fileSystemEntry.name = fileMetaData.fileName;
fileSystemEntry.fileOffset = fileMetaData.fileDataRealOffset; fileSystemEntry.offset = fileMetaData.fileDataRealOffset;
fileSystemEntry.fileSize = fileMetaData.fileDataRealLength; fileSystemEntry.size = fileMetaData.fileDataRealLength;
if (fileMetaData.nextSiblingFileOffset != -1) if (fileMetaData.nextSiblingFileOffset != -1)
directoryContainer.content.add(getFile(directoryContainer, fileMetaData.nextSiblingFileOffset) ); directoryContainer.content.add(getFile(directoryContainer, fileMetaData.nextSiblingFileOffset) );
@ -106,8 +106,8 @@ public class FileSystemEntry {
public boolean isDirectory() { return directoryFlag; } public boolean isDirectory() { return directoryFlag; }
public boolean isFile() { return ! directoryFlag; } public boolean isFile() { return ! directoryFlag; }
public long getFileOffset() { return fileOffset; } public long getOffset() { return offset; }
public long getFileSize() { return fileSize; } public long getSize() { return size; }
public List<FileSystemEntry> getContent() { return content; } public List<FileSystemEntry> getContent() { return content; }
public String getName(){ return name; } public String getName(){ return name; }
@ -135,7 +135,7 @@ public class FileSystemEntry {
firstFileOffset = Converter.getLEint(dirsMetadataTable, i); firstFileOffset = Converter.getLEint(dirsMetadataTable, i);
i += 4; i += 4;
nextHashTableBucketDirectoryOffset = Converter.getLEint(dirsMetadataTable, i); nextHashTableBucketDirectoryOffset = Converter.getLEint(dirsMetadataTable, i);
//* /*
if (nextHashTableBucketDirectoryOffset < 0) { if (nextHashTableBucketDirectoryOffset < 0) {
System.out.println("nextHashTableBucketDirectoryOffset: "+ nextHashTableBucketDirectoryOffset); System.out.println("nextHashTableBucketDirectoryOffset: "+ nextHashTableBucketDirectoryOffset);
} }
@ -183,7 +183,7 @@ public class FileSystemEntry {
fileDataRealLength = Converter.getLElong(filesMetadataTable, i); fileDataRealLength = Converter.getLElong(filesMetadataTable, i);
i += 8; i += 8;
nextHashTableBucketFileOffset = Converter.getLEint(filesMetadataTable, i); nextHashTableBucketFileOffset = Converter.getLEint(filesMetadataTable, i);
//* /*
if (nextHashTableBucketFileOffset < 0) { if (nextHashTableBucketFileOffset < 0) {
System.out.println("nextHashTableBucketFileOffset: "+ nextHashTableBucketFileOffset); System.out.println("nextHashTableBucketFileOffset: "+ nextHashTableBucketFileOffset);
} }

View File

@ -91,16 +91,16 @@ public class Level6Header {
public void printDebugInfo(){ public void printDebugInfo(){
log.debug("== Level 6 Header ==\n" + log.debug("== Level 6 Header ==\n" +
"Header Length (usually 0x50) "+ RainbowDump.formatDecHexString(headerLength)+" (size of this structure within first 0x200 block of LEVEL 6 part)\n" + "Header Length (usually 0x50) "+ RainbowDump.formatDecHexString(headerLength)+" (size of this structure within first 0x200 block of LEVEL 6 part)\n" +
"Directory Hash Table Offset "+ RainbowDump.formatDecHexString(directoryHashTableOffset)+" (against THIS block where HEADER contains)\n" + "Directory Hash Table Offset "+ RainbowDump.formatDecHexString(directoryHashTableOffset)+" (against THIS block where HEADER contains)\n" +
"Directory Hash Table Length "+ RainbowDump.formatDecHexString(directoryHashTableLength) + "\n" + "Directory Hash Table Length "+ RainbowDump.formatDecHexString(directoryHashTableLength) + "\n" +
"Directory Metadata Table Offset "+ RainbowDump.formatDecHexString(directoryMetadataTableOffset) + "\n" + "Directory Metadata Table Offset "+ RainbowDump.formatDecHexString(directoryMetadataTableOffset) + "\n" +
"Directory Metadata Table Length "+ RainbowDump.formatDecHexString(directoryMetadataTableLength) + "\n" + "Directory Metadata Table Length "+ RainbowDump.formatDecHexString(directoryMetadataTableLength) + "\n" +
"File Hash Table Offset "+ RainbowDump.formatDecHexString(fileHashTableOffset) + "\n" + "File Hash Table Offset "+ RainbowDump.formatDecHexString(fileHashTableOffset) + "\n" +
"File Hash Table Length "+ RainbowDump.formatDecHexString(fileHashTableLength) + "\n" + "File Hash Table Length "+ RainbowDump.formatDecHexString(fileHashTableLength) + "\n" +
"File Metadata Table Offset "+ RainbowDump.formatDecHexString(fileMetadataTableOffset) + "\n" + "File Metadata Table Offset "+ RainbowDump.formatDecHexString(fileMetadataTableOffset) + "\n" +
"File Metadata Table Length "+ RainbowDump.formatDecHexString(fileMetadataTableLength) + "\n" + "File Metadata Table Length "+ RainbowDump.formatDecHexString(fileMetadataTableLength) + "\n" +
"File Data Offset "+ RainbowDump.formatDecHexString(fileDataOffset) + "\n" + "File Data Offset "+ RainbowDump.formatDecHexString(fileDataOffset) + "\n" +
"-------------------------------------------------------------" "-------------------------------------------------------------"
); );
} }

View File

@ -58,8 +58,8 @@ public class RomFsDecryptedProvider implements IRomFsProvider{
PipedOutputStream streamOut = new PipedOutputStream(); PipedOutputStream streamOut = new PipedOutputStream();
PipedInputStream streamIn = new PipedInputStream(streamOut); PipedInputStream streamIn = new PipedInputStream(streamOut);
long internalFileRealPosition = level6Offset + level6Header.getFileDataOffset() + entry.getFileOffset(); long internalFileRealPosition = level6Offset + level6Header.getFileDataOffset() + entry.getOffset();
long internalFileSize = entry.getFileSize(); long internalFileSize = entry.getSize();
Thread contentRetrievingThread = new Thread( Thread contentRetrievingThread = new Thread(
new RomFsDecryptedContentRetrieve(file, streamOut, internalFileRealPosition, internalFileSize)); new RomFsDecryptedContentRetrieve(file, streamOut, internalFileRealPosition, internalFileSize));

View File

@ -47,6 +47,7 @@ public class RomFsEncryptedContentRetrieve implements Runnable{
long level6Offset, long level6Offset,
long headersFileDataOffset long headersFileDataOffset
){ ){
log.fatal("Current implementation works incorrectly");
this.parentFile = parentFile; this.parentFile = parentFile;
this.absoluteOffsetPosition = absoluteOffsetPosition; this.absoluteOffsetPosition = absoluteOffsetPosition;
this.streamOut = streamOut; this.streamOut = streamOut;

View File

@ -75,7 +75,6 @@ public class RomFsEncryptedProvider implements IRomFsProvider{
this.level6Offset = level6Offset; this.level6Offset = level6Offset;
this.level6Header = construct.getHeader(); this.level6Header = construct.getHeader();
this.rootEntry = construct.getRootEntry(); this.rootEntry = construct.getRootEntry();
this.absoluteOffsetPosition = romFsOffsetPosition + (mediaStartOffset * 0x200); this.absoluteOffsetPosition = romFsOffsetPosition + (mediaStartOffset * 0x200);
this.directoryMetadataTable = construct.getDirectoryMetadataTable(); this.directoryMetadataTable = construct.getDirectoryMetadataTable();
@ -99,8 +98,8 @@ public class RomFsEncryptedProvider implements IRomFsProvider{
PipedOutputStream streamOut = new PipedOutputStream(); PipedOutputStream streamOut = new PipedOutputStream();
PipedInputStream streamIn = new PipedInputStream(streamOut); PipedInputStream streamIn = new PipedInputStream(streamOut);
long internalFileOffset = entry.getFileOffset(); long internalFileOffset = entry.getOffset();
long internalFileSize = entry.getFileSize(); long internalFileSize = entry.getSize();
Thread contentRetrievingThread = new Thread(new RomFsEncryptedContentRetrieve( Thread contentRetrievingThread = new Thread(new RomFsEncryptedContentRetrieve(
file, file,

View File

@ -52,7 +52,7 @@ public class FileSystemTreeViewMaker {
tree.append("|-"); tree.append("|-");
tree.append(entry.getName()); tree.append(entry.getName());
tree.append(String.format("%"+(spacerForSizes-entry.getName().length()-i)+"s0x%-10x 0x%-10x", "", entry.getFileOffset(), entry.getFileSize())); tree.append(String.format("%"+(spacerForSizes-entry.getName().length()-i)+"s0x%-10x 0x%-10x", "", entry.getOffset(), entry.getSize()));
tree.append("\n"); tree.append("\n");
} }

View File

@ -59,7 +59,6 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
log.trace("1.2. Pointer Inside + End Position Inside (Decrypted) Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")"); log.trace("1.2. Pointer Inside + End Position Inside (Decrypted) Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesToRead); System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesToRead);
log.error("!Pointer Inside Decrypted Section "+pointerInsideDecryptedSection+" "+(pointerInsideDecryptedSection+bytesToRead));
pseudoPos += bytesToRead; pseudoPos += bytesToRead;
pointerInsideDecryptedSection += bytesToRead; pointerInsideDecryptedSection += bytesToRead;
return b.length; return b.length;
@ -84,12 +83,12 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
return b.length; return b.length;
} }
log.trace("1. Pointer Inside + End Position Outside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")"); log.trace("1. Pointer Inside + End Position Outside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
int middleBlocksCount = (int) ((mediaOffsetPositionEnd - pseudoPos) / 0x200); int middleBlocksCount = (int) ((mediaOffsetPositionEnd - (pseudoPos+bytesFromFirstBlock)) / 0x200);
int bytesFromEnd = b.length - bytesFromFirstBlock - middleBlocksCount * 0x200; int bytesFromEnd = bytesToRead - bytesFromFirstBlock - middleBlocksCount * 0x200;
//1 //1
System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock); System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock);
//2 //2
//System.out.println("\n"+bytesFromFirstBlock+"\n"+ middleBlocksCount+" - "+(middleBlocksCount*0x200)+"\n"+ bytesFromEnd+"\n"); System.out.println("\n"+bytesFromFirstBlock+"\n"+ middleBlocksCount+" = "+(middleBlocksCount*0x200)+" bytes\n"+ bytesFromEnd+"\n");
for (int i = 0; i < middleBlocksCount; i++) { for (int i = 0; i < middleBlocksCount; i++) {
fillDecryptedCache(); fillDecryptedCache();
System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200); System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200);
@ -102,7 +101,7 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
} }
if (isEndPositionInsideEncryptedSection(bytesToRead)) { if (isEndPositionInsideEncryptedSection(bytesToRead)) {
log.trace("2. End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")"); log.trace("2. End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
int bytesTillEncrypted = (int) (mediaOffsetPositionStart - pos); int bytesTillEncrypted = (int) (mediaOffsetPositionStart - pseudoPos);
int fullEncryptedBlocks = (bytesToRead - bytesTillEncrypted) / 0x200; int fullEncryptedBlocks = (bytesToRead - bytesTillEncrypted) / 0x200;
int incompleteEncryptedBytes = (bytesToRead - bytesTillEncrypted) % 0x200; int incompleteEncryptedBytes = (bytesToRead - bytesTillEncrypted) % 0x200;
System.arraycopy(readChunk(bytesTillEncrypted), 0, b, 0, bytesTillEncrypted); System.arraycopy(readChunk(bytesTillEncrypted), 0, b, 0, bytesTillEncrypted);
@ -150,10 +149,10 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
} }
private boolean isPointerInsideEncryptedSection(){ private boolean isPointerInsideEncryptedSection(){
return (pseudoPos >= mediaOffsetPositionStart) && (pseudoPos < mediaOffsetPositionEnd); return (pseudoPos-pointerInsideDecryptedSection >= mediaOffsetPositionStart) && (pseudoPos-pointerInsideDecryptedSection < mediaOffsetPositionEnd);
} }
private boolean isEndPositionInsideEncryptedSection(long requestedBytesCount){ private boolean isEndPositionInsideEncryptedSection(long requestedBytesCount){
return ((pseudoPos + requestedBytesCount) >= mediaOffsetPositionStart) && ((pseudoPos + requestedBytesCount) < mediaOffsetPositionEnd); return ((pseudoPos-pointerInsideDecryptedSection + requestedBytesCount) >= mediaOffsetPositionStart) && ((pseudoPos-pointerInsideDecryptedSection + requestedBytesCount) < mediaOffsetPositionEnd);
} }
@Override @Override
@ -199,7 +198,7 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
if (isEndPositionInsideEncryptedSection(n)) { //pointer will be inside Encrypted Section, but now outside if (isEndPositionInsideEncryptedSection(n)) { //pointer will be inside Encrypted Section, but now outside
log.trace("5. End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+n)+")"); log.trace("5. End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+n)+")");
//skip to start if the block we need //skip to start if the block we need
long bytesToSkipTillEncryptedBlock = mediaOffsetPositionStart - pseudoPos; //TODO:FIX long bytesToSkipTillEncryptedBlock = mediaOffsetPositionStart - pseudoPos;
long blocksToSkipCountingFromStart = (n - bytesToSkipTillEncryptedBlock) / 0x200; // always positive long blocksToSkipCountingFromStart = (n - bytesToSkipTillEncryptedBlock) / 0x200; // always positive
long bytesToSkipTillRequiredBlock = bytesToSkipTillEncryptedBlock + blocksToSkipCountingFromStart * 0x200; long bytesToSkipTillRequiredBlock = bytesToSkipTillEncryptedBlock + blocksToSkipCountingFromStart * 0x200;
long leftovers = n - bytesToSkipTillRequiredBlock; // most likely will be 0; long leftovers = n - bytesToSkipTillRequiredBlock; // most likely will be 0;
@ -210,10 +209,12 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
bytesToSkipTillEncryptedBlock + bytesToSkipTillEncryptedBlock +
".\nActually skipped: " + skipped + ".\nActually skipped: " + skipped +
".\nLeftovers inside encrypted section: " + leftovers); ".\nLeftovers inside encrypted section: " + leftovers);
log.trace("\tBlocks skipped "+blocksToSkipCountingFromStart);
resetAndSkip(blocksToSkipCountingFromStart); resetAndSkip(blocksToSkipCountingFromStart);
fillDecryptedCache(); fillDecryptedCache();
pseudoPos += n; pseudoPos += n;
pointerInsideDecryptedSection = (int) leftovers; pointerInsideDecryptedSection = (int) leftovers;
log.debug(" "+pseudoPos+" "+pointerInsideDecryptedSection);
return n; return n;
} }
log.trace("6. Not encrypted ("+pseudoPos+"-"+(pseudoPos+n)+")"); log.trace("6. Not encrypted ("+pseudoPos+"-"+(pseudoPos+n)+")");

View File

@ -19,7 +19,6 @@
package libKonogonka.RomFsDecrypted; package libKonogonka.RomFsDecrypted;
import libKonogonka.KeyChainHolder; import libKonogonka.KeyChainHolder;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -32,7 +31,7 @@ public class KeyChainHolderTest {
private static final String xci_header_keyFileLocation = "./FilesForTests/xci_header_key.txt"; private static final String xci_header_keyFileLocation = "./FilesForTests/xci_header_key.txt";
private KeyChainHolder keyChainHolder; private KeyChainHolder keyChainHolder;
@Disabled //@Disabled
@DisplayName("Key Chain Holder Test") @DisplayName("Key Chain Holder Test")
@Test @Test
void keysChain() throws Exception{ void keysChain() throws Exception{