start working on encrypted sections extractor
This commit is contained in:
parent
e2f7f93988
commit
dd6c81c7fd
8 changed files with 276 additions and 155 deletions
|
@ -7,6 +7,7 @@ import javafx.scene.layout.HBox;
|
|||
import javafx.scene.layout.VBox;
|
||||
import konogonka.Controllers.NSP.NSPController;
|
||||
import konogonka.LoperConverter;
|
||||
import konogonka.Tools.PFS0.IPFS0Provider;
|
||||
import konogonka.Tools.PFS0.PFS0Provider;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -23,7 +24,7 @@ public class NCASectionContentController{
|
|||
sha256pane.getChildren().clear();
|
||||
}
|
||||
|
||||
public void populateFields(PFS0Provider pfs0, File file, LinkedList<byte[]> sha256hashList) {
|
||||
public void populateFields(IPFS0Provider pfs0, File file, LinkedList<byte[]> sha256hashList) {
|
||||
resetTab();
|
||||
SectionPFS0Controller.setData(pfs0, file);
|
||||
if (sha256hashList != null){
|
||||
|
|
|
@ -6,6 +6,7 @@ import javafx.scene.control.Label;
|
|||
import konogonka.Controllers.IRowModel;
|
||||
import konogonka.Controllers.TabController;
|
||||
import konogonka.MediatorControl;
|
||||
import konogonka.Tools.PFS0.IPFS0Provider;
|
||||
import konogonka.Tools.PFS0.PFS0Provider;
|
||||
import konogonka.Workers.AnalyzerNSP;
|
||||
import konogonka.Workers.NspXciExtractor;
|
||||
|
@ -102,7 +103,7 @@ public class NSPController implements TabController {
|
|||
/**
|
||||
* Just populate fields by already analyzed PFS0
|
||||
* */
|
||||
public void setData(PFS0Provider pfs0, File fileWithNca){
|
||||
public void setData(IPFS0Provider pfs0, File fileWithNca){
|
||||
if (pfs0 != null){
|
||||
if (fileWithNca != null)
|
||||
this.selectedFile = fileWithNca;
|
||||
|
|
|
@ -17,6 +17,7 @@ import javafx.scene.input.MouseButton;
|
|||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.util.Callback;
|
||||
import konogonka.Controllers.IRowModel;
|
||||
import konogonka.Tools.PFS0.IPFS0Provider;
|
||||
import konogonka.Tools.PFS0.PFS0Provider;
|
||||
|
||||
import java.net.URL;
|
||||
|
@ -144,7 +145,7 @@ public class Pfs0TableViewController implements Initializable {
|
|||
/**
|
||||
* Add files when user selected them
|
||||
* */
|
||||
public void setNSPToTable(PFS0Provider pfs){
|
||||
public void setNSPToTable(IPFS0Provider pfs){
|
||||
rowsObsLst.clear();
|
||||
Pfs0RowModel.resetNumCnt();
|
||||
if (pfs == null) {
|
||||
|
|
|
@ -3,6 +3,8 @@ package konogonka.Tools.NCA;
|
|||
import konogonka.LoperConverter;
|
||||
import konogonka.RainbowHexDump;
|
||||
import konogonka.Tools.NCA.NCASectionTableBlock.NCASectionBlock;
|
||||
import konogonka.Tools.PFS0.IPFS0Provider;
|
||||
import konogonka.Tools.PFS0.PFS0EncryptedProvider;
|
||||
import konogonka.Tools.PFS0.PFS0Provider;
|
||||
import konogonka.ctraes.AesCtr;
|
||||
|
||||
|
@ -11,14 +13,14 @@ import java.util.LinkedList;
|
|||
|
||||
public class NCAContentPFS0 {
|
||||
private LinkedList<byte[]> SHA256hashes;
|
||||
private PFS0Provider pfs0;
|
||||
private IPFS0Provider pfs0;
|
||||
|
||||
// TODO: if decryptedKey is empty, thorow exception ??
|
||||
public NCAContentPFS0(File file, long offsetPosition, NCASectionBlock ncaSectionBlock, NCAHeaderTableEntry ncaHeaderTableEntry, byte[] decryptedKey){
|
||||
SHA256hashes = new LinkedList<>();
|
||||
try {
|
||||
// If it's PFS0Provider
|
||||
if (ncaSectionBlock.getSuperBlockPFS0() != null){
|
||||
try {
|
||||
// IF NO ENCRYPTION
|
||||
if (ncaSectionBlock.getCryptoType() == 0x1) {
|
||||
RandomAccessFile raf = new RandomAccessFile(file, "r");
|
||||
|
@ -35,15 +37,17 @@ public class NCAContentPFS0 {
|
|||
rawData = new byte[0x20]; // 32 bytes - size of SHA256 hash
|
||||
if (raf.read(rawData) != -1)
|
||||
SHA256hashes.add(rawData);
|
||||
else
|
||||
else {
|
||||
raf.close();
|
||||
return; // TODO: fix
|
||||
}
|
||||
}
|
||||
raf.close();
|
||||
// Get pfs0
|
||||
pfs0 = new PFS0Provider(file, pfs0Location);
|
||||
}
|
||||
// If encrypted (regular)
|
||||
else if (ncaSectionBlock.getCryptoType() == 0x3){ // d0c1...
|
||||
else if (ncaSectionBlock.getCryptoType() == 0x3){
|
||||
new CryptoSection03(file,
|
||||
offsetPosition,
|
||||
decryptedKey,
|
||||
|
@ -52,25 +56,25 @@ public class NCAContentPFS0 {
|
|||
ncaHeaderTableEntry.getMediaEndOffset());
|
||||
}
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else if (ncaSectionBlock.getSuperBlockIVFC() != null){
|
||||
|
||||
// TODO
|
||||
}
|
||||
else {
|
||||
return; // TODO: FIX THIS STUFF
|
||||
}
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public LinkedList<byte[]> getSHA256hashes() { return SHA256hashes; }
|
||||
public PFS0Provider getPfs0() { return pfs0; }
|
||||
public IPFS0Provider getPfs0() { return pfs0; }
|
||||
|
||||
private class CryptoSection03{
|
||||
|
||||
CryptoSection03(File file, long offsetPosition, byte[] decryptedKey, NCASectionBlock ncaSectionBlock, long mediaStartOffset, long mediaEndOffset) throws Exception{
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/*//--------------------------------------------------------------------------------------------------
|
||||
System.out.println("Media start location: " + mediaStartOffset);
|
||||
System.out.println("Media end location: " + mediaEndOffset);
|
||||
System.out.println("Media size : " + (mediaEndOffset-mediaStartOffset));
|
||||
|
@ -82,13 +86,13 @@ public class NCAContentPFS0 {
|
|||
System.out.println("KEY: " + LoperConverter.byteArrToHexString(decryptedKey));
|
||||
System.out.println("CTR: " + LoperConverter.byteArrToHexString(ncaSectionBlock.getSectionCTR()));
|
||||
System.out.println();
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------------------------------------------*/
|
||||
if (decryptedKey == null)
|
||||
throw new Exception("CryptoSection03: unable to proceed. No decrypted key provided.");
|
||||
|
||||
RandomAccessFile raf = new RandomAccessFile(file, "r");
|
||||
raf.seek(offsetPosition + (mediaStartOffset * 0x200));
|
||||
long abosluteOffsetPosition = offsetPosition + (mediaStartOffset * 0x200);
|
||||
raf.seek(abosluteOffsetPosition);
|
||||
|
||||
AesCtrDecryptor decryptor = new AesCtrDecryptor(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartOffset * 0x200);
|
||||
|
||||
|
@ -99,13 +103,14 @@ public class NCAContentPFS0 {
|
|||
PipedOutputStream streamOut = new PipedOutputStream();
|
||||
PipedInputStream streamInp = new PipedInputStream(streamOut);
|
||||
|
||||
new Thread(new ParseThread(
|
||||
Thread pThread = new Thread(new ParseThread(
|
||||
streamInp,
|
||||
ncaSectionBlock.getSuperBlockPFS0().getPfs0offset(),
|
||||
ncaSectionBlock.getSuperBlockPFS0().getPfs0size(),
|
||||
ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset(),
|
||||
ncaSectionBlock.getSuperBlockPFS0().getHashTableSize()
|
||||
)).start();
|
||||
));
|
||||
pThread.start();
|
||||
// Decrypt data
|
||||
for (int i = 0; i < mediaBlockSize; i++){
|
||||
encryptedBlock = new byte[0x200];
|
||||
|
@ -113,12 +118,16 @@ public class NCAContentPFS0 {
|
|||
//dectyptedBlock = aesCtr.decrypt(encryptedBlock);
|
||||
dectyptedBlock = decryptor.dectyptNext(encryptedBlock);
|
||||
// Writing decrypted data to pipe
|
||||
try {
|
||||
streamOut.write(dectyptedBlock);
|
||||
}
|
||||
catch (IOException e){
|
||||
break;
|
||||
}
|
||||
streamOut.flush();
|
||||
}
|
||||
}
|
||||
pThread.join();
|
||||
streamOut.close();
|
||||
|
||||
raf.close();
|
||||
}
|
||||
/*
|
||||
|
@ -187,8 +196,6 @@ public class NCAContentPFS0 {
|
|||
return; // TODO: fix?
|
||||
counter = hashTableOffset;
|
||||
}
|
||||
// Main loop
|
||||
while (true){
|
||||
// Loop for collecting all recrods from sha256 hash table
|
||||
while ((counter - hashTableOffset) < hashTableSize){
|
||||
int hashCounter = 0;
|
||||
|
@ -213,25 +220,15 @@ public class NCAContentPFS0 {
|
|||
counter += toSkip;
|
||||
}
|
||||
//---------------------------------------------------------
|
||||
byte[] magic = new byte[0x4];
|
||||
for (int i = 0; i < 4; i++){
|
||||
int currentByte = pipedInputStream.read();
|
||||
if (currentByte == -1)
|
||||
break;
|
||||
magic[i] = (byte)currentByte;
|
||||
pfs0 = new PFS0EncryptedProvider(pipedInputStream);
|
||||
pipedInputStream.close();
|
||||
}
|
||||
RainbowHexDump.hexDumpUTF8(magic);
|
||||
while (pipedInputStream.read() != -1)
|
||||
;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (IOException ioe){
|
||||
catch (Exception e){
|
||||
System.out.println("'ParseThread' thread exception");
|
||||
ioe.printStackTrace();
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
System.out.println("ParseThread thread died.");
|
||||
System.out.println("Thread dies");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -267,6 +267,7 @@ public class NCAProvider {
|
|||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
System.out.println("No title.keys loaded?");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
11
src/main/java/konogonka/Tools/PFS0/IPFS0Provider.java
Normal file
11
src/main/java/konogonka/Tools/PFS0/IPFS0Provider.java
Normal file
|
@ -0,0 +1,11 @@
|
|||
package konogonka.Tools.PFS0;
|
||||
|
||||
public interface IPFS0Provider {
|
||||
String getMagic();
|
||||
int getFilesCount();
|
||||
int getStringTableSize();
|
||||
byte[] getPadding();
|
||||
|
||||
long getRawFileDataStart();
|
||||
PFS0subFile[] getPfs0subFiles();
|
||||
}
|
114
src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java
Normal file
114
src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java
Normal file
|
@ -0,0 +1,114 @@
|
|||
package konogonka.Tools.PFS0;
|
||||
|
||||
import java.io.PipedInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static konogonka.LoperConverter.getLEint;
|
||||
import static konogonka.LoperConverter.getLElong;
|
||||
|
||||
public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||
private long rawFileDataStart; // If -1 then this PFS0 located @ encrypted region
|
||||
|
||||
private String magic;
|
||||
private int filesCount;
|
||||
private int stringTableSize;
|
||||
private byte[] padding;
|
||||
private PFS0subFile[] pfs0subFiles;
|
||||
|
||||
//---------------------------------------
|
||||
/*
|
||||
absOffsetPosOfMediaBlock
|
||||
|
||||
Counter - PFS0 Position
|
||||
mediaBlockSize - PFS0 Subsustem Size
|
||||
* */
|
||||
//---------------------------------------
|
||||
|
||||
public PFS0EncryptedProvider(PipedInputStream pipedInputStream) throws Exception{
|
||||
byte[] fileStartingBytes = new byte[0x10];
|
||||
// Read PFS0Provider, files count, header, padding (4 zero bytes)
|
||||
|
||||
for (int i = 0; i < 0x10; i++){
|
||||
int currentByte = pipedInputStream.read();
|
||||
if (currentByte == -1) {
|
||||
throw new Exception("PFS0: Reading stream suddenly ended while trying to read starting 0x10 bytes");
|
||||
}
|
||||
fileStartingBytes[i] = (byte)currentByte;
|
||||
}
|
||||
// Check PFS0Provider
|
||||
magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII);
|
||||
if (! magic.equals("PFS0")){
|
||||
throw new Exception("PFS0Provider: Bad magic");
|
||||
}
|
||||
// Get files count
|
||||
filesCount = getLEint(fileStartingBytes, 0x4);
|
||||
if (filesCount <= 0 ) {
|
||||
throw new Exception("PFS0Provider: Files count is too small");
|
||||
}
|
||||
// Get string table
|
||||
stringTableSize = getLEint(fileStartingBytes, 0x8);
|
||||
if (stringTableSize <= 0 ){
|
||||
throw new Exception("PFS0Provider: String table is too small");
|
||||
}
|
||||
padding = Arrays.copyOfRange(fileStartingBytes, 0xc, 0x10);
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
pfs0subFiles = new PFS0subFile[filesCount];
|
||||
|
||||
long[] offsetsSubFiles = new long[filesCount];
|
||||
long[] sizesSubFiles = new long[filesCount];
|
||||
int[] strTableOffsets = new int[filesCount];
|
||||
byte[][] zeroBytes = new byte[filesCount][];
|
||||
|
||||
byte[] fileEntryTable = new byte[0x18];
|
||||
for (int i=0; i < filesCount; i++){
|
||||
for (int j = 0; j < 0x18; j++){
|
||||
int currentByte = pipedInputStream.read();
|
||||
if (currentByte == -1) {
|
||||
throw new Exception("PFS0: Reading stream suddenly ended while trying to read File Entry Table #"+i);
|
||||
}
|
||||
fileEntryTable[j] = (byte)currentByte;
|
||||
}
|
||||
offsetsSubFiles[i] = getLElong(fileEntryTable, 0);
|
||||
sizesSubFiles[i] = getLElong(fileEntryTable, 0x8);
|
||||
strTableOffsets[i] = getLEint(fileEntryTable, 0x10);
|
||||
zeroBytes[i] = Arrays.copyOfRange(fileEntryTable, 0x14, 0x18);
|
||||
}
|
||||
//**********************************************************************************************************
|
||||
// In here pointer in front of String table
|
||||
String[] subFileNames = new String[filesCount];
|
||||
byte[] stringTbl = new byte[stringTableSize];
|
||||
|
||||
for (int i = 0; i < stringTableSize; i++){
|
||||
int currentByte = pipedInputStream.read();
|
||||
if (currentByte == -1) {
|
||||
throw new Exception("PFS0: Reading stream suddenly ended while trying to read string table");
|
||||
}
|
||||
stringTbl[i] = (byte)currentByte;
|
||||
}
|
||||
|
||||
for (int i=0; i < filesCount; i++){
|
||||
int j = 0;
|
||||
while (stringTbl[strTableOffsets[i]+j] != (byte)0x00)
|
||||
j++;
|
||||
subFileNames[i] = new String(stringTbl, strTableOffsets[i], j, StandardCharsets.UTF_8);
|
||||
}
|
||||
for (int i = 0; i < filesCount; i++){
|
||||
pfs0subFiles[i] = new PFS0subFile(
|
||||
subFileNames[i],
|
||||
offsetsSubFiles[i],
|
||||
sizesSubFiles[i],
|
||||
zeroBytes[i]
|
||||
);
|
||||
}
|
||||
rawFileDataStart = -1;
|
||||
}
|
||||
|
||||
public String getMagic() { return magic; }
|
||||
public int getFilesCount() { return filesCount; }
|
||||
public int getStringTableSize() { return stringTableSize; }
|
||||
public byte[] getPadding() { return padding; }
|
||||
|
||||
public long getRawFileDataStart() { return rawFileDataStart; }
|
||||
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
|
||||
}
|
|
@ -3,15 +3,14 @@ package konogonka.Tools.PFS0;
|
|||
import konogonka.RainbowHexDump;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static konogonka.LoperConverter.*;
|
||||
|
||||
public class PFS0Provider {
|
||||
private long rawFileDataStart;
|
||||
public class PFS0Provider implements IPFS0Provider{
|
||||
private long rawFileDataStart; // If -1 then this PFS0 located @ encrypted region
|
||||
|
||||
private String magic;
|
||||
private int filesCount;
|
||||
|
@ -22,8 +21,8 @@ public class PFS0Provider {
|
|||
public PFS0Provider(File fileWithPfs0) throws Exception{ this(fileWithPfs0, 0); }
|
||||
|
||||
public PFS0Provider(File fileWithPfs0, long pfs0offsetPosition) throws Exception{
|
||||
try {
|
||||
RandomAccessFile raf = new RandomAccessFile(fileWithPfs0, "r");
|
||||
|
||||
RandomAccessFile raf = new RandomAccessFile(fileWithPfs0, "r"); // TODO: replace to bufferedInputStream
|
||||
|
||||
raf.seek(pfs0offsetPosition);
|
||||
byte[] fileStartingBytes = new byte[0x10];
|
||||
|
@ -93,10 +92,6 @@ public class PFS0Provider {
|
|||
rawFileDataStart = raf.getFilePointer();
|
||||
raf.close();
|
||||
}
|
||||
catch (IOException ioe){
|
||||
throw new IOException("PFS0Provider: "+ioe.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String getMagic() { return magic; }
|
||||
public int getFilesCount() { return filesCount; }
|
||||
|
|
Loading…
Reference in a new issue