Small fixes, minor updates.

master
Dmitry Isaenko 2022-12-07 04:22:23 +03:00
parent f058905e2f
commit 51398a6ea9
11 changed files with 373 additions and 106 deletions

View File

@ -6,4 +6,13 @@ mvn install:install-file \
-DartifactId=libKonogonka \
-Dversion=0.1 \
-Dpackaging=jar \
-DgeneratePom=true
-DgeneratePom=true
-Dlog4j.configurationFile=/home/loper/Projects/libKonogonka/src/main/resources/log4j2.properties
- NCA flow:
NCAProvider -> NCAContent -> RomFsEncryptedProvider (needs to know location of the lv6header location)
Reads Section Block to understand boundaries Reads lv6Header via \RomFsEncryptedConstruct'
Reads Header Table Entry to get meta info 4 section Provides stream/streams with requested files via 'RomFsEncrytedContenRetrieve'

View File

@ -2,7 +2,11 @@
![License](https://img.shields.io/badge/License-GPLv3-blue.svg) [![Build Status](https://ci.redrise.ru/api/badges/desu/libKonogonka/status.svg)](https://ci.redrise.ru/desu/libKonogonka)
Library to work with NS-specific files / filesystem images. Ex-backend of [konogonka](https://github.com/developersu/konogonka) ([independent src location](https://git.redrise.ru/desu/konogonka))
Library to work with NS-specific files / filesystem images. Ex-backend of [konogonka](https://github.com/developersu/konogonka) ([independent source location](https://git.redrise.ru/desu/konogonka))
### Let's stay in touch
You can get this application from independent source location: [https://git.redrise.ru/desu/libKonogonka](https://git.redrise.ru/desu/libKonogonka)
### License

View File

@ -18,11 +18,18 @@
*/
package libKonogonka;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class Converter {
private final static Logger log = LogManager.getLogger(Converter.class);
public static int getLEint(byte[] bytes, int fromOffset){
if (fromOffset < 0 || fromOffset >= bytes.length)
log.debug("\tLen =" + bytes.length + "\tFrom =" + fromOffset);
return ByteBuffer.wrap(bytes, fromOffset, 0x4).order(ByteOrder.LITTLE_ENDIAN).getInt();
}

View File

@ -18,12 +18,17 @@
*/
package libKonogonka;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.nio.charset.StandardCharsets;
/**
* Debug tool like hexdump <3
*/
public class RainbowDump {
private final static Logger log = LogManager.getLogger(Converter.class);
private static final String ANSI_RESET = "\u001B[0m";
private static final String ANSI_BLACK = "\u001B[30m";
private static final String ANSI_RED = "\u001B[31m";
@ -34,51 +39,56 @@ public class RainbowDump {
private static final String ANSI_CYAN = "\u001B[36m";
private static final String ANSI_WHITE = "\u001B[37m";
private static StringBuilder stringBuilder;
public static void hexDumpUTF8(byte[] byteArray){
stringBuilder = new StringBuilder();
if (byteArray == null || byteArray.length == 0)
return;
int k = 0;
System.out.printf("%s%08x %s", ANSI_BLUE, 0, ANSI_RESET);
stringBuilder.append(String.format("%s%08x %s", ANSI_BLUE, 0, ANSI_RESET));
for (int i = 0; i < byteArray.length; i++) {
if (k == 8)
System.out.print(" ");
stringBuilder.append(" ");
if (k == 16){
System.out.print(ANSI_GREEN+"| "+ANSI_RESET);
stringBuilder.append(ANSI_GREEN+"| "+ANSI_RESET);
printChars(byteArray, i);
System.out.println();
System.out.printf("%s%08x %s", ANSI_BLUE, i, ANSI_RESET);
stringBuilder.append("\n")
.append(String.format("%s%08x %s", ANSI_BLUE, i, ANSI_RESET));
k = 0;
}
System.out.printf("%02x ", byteArray[i]);
stringBuilder.append(String.format("%02x ", byteArray[i]));
k++;
}
int paddingSize = 16 - (byteArray.length % 16);
if (paddingSize != 16) {
for (int i = 0; i < paddingSize; i++) {
System.out.print(" ");
stringBuilder.append(" ");
}
if (paddingSize > 7) {
System.out.print(" ");
stringBuilder.append(" ");
}
}
System.out.print(ANSI_GREEN+"| "+ANSI_RESET);
stringBuilder.append(ANSI_GREEN+"| "+ANSI_RESET);
printChars(byteArray, byteArray.length);
System.out.println();
System.out.print(ANSI_RESET+new String(byteArray, StandardCharsets.UTF_8)+"\n");
stringBuilder.append("\n")
.append(ANSI_RESET)
.append(new String(byteArray, StandardCharsets.UTF_8))
.append("\n");
log.debug(stringBuilder.toString());
}
private static void printChars(byte[] byteArray, int pointer){
for (int j = pointer-16; j < pointer; j++){
if ((byteArray[j] > 21) && (byteArray[j] < 126)) // man ascii
System.out.print((char) byteArray[j]);
stringBuilder.append((char) byteArray[j]);
else if (byteArray[j] == 0x0a)
System.out.print("↲"); //"␤"
stringBuilder.append("↲"); //"␤"
else if (byteArray[j] == 0x0d)
System.out.print("←"); // "␍"
stringBuilder.append("←"); // "␍"
else
System.out.print(".");
stringBuilder.append(".");
}
}
@ -107,4 +117,7 @@ public class RainbowDump {
public static String formatDecHexString(long value){
return String.format("%-20d 0x%x", value, value);
}
public static String formatDecHexString(int value){
return String.format("%-20d 0x%x", value, value);
}
}

View File

@ -18,6 +18,7 @@
*/
package libKonogonka.Tools.NCA;
import libKonogonka.Converter;
import libKonogonka.Tools.NCA.NCASectionTableBlock.NcaFsHeader;
import libKonogonka.exceptions.EmptySectionException;
import libKonogonka.xtsaes.XTSAESCipher;
@ -45,7 +46,7 @@ public class NCAProvider {
// Header
private byte[] rsa2048one;
private byte[] rsa2048two;
private String magicnum;
private String magicNumber;
private byte systemOrGcIndicator;
private byte contentType;
private byte cryptoType1; // keyblob index. Considering as number within application/ocean/system
@ -61,41 +62,18 @@ public class NCAProvider {
private byte cryptoTypeReal;
private byte[] sha256hash0;
private byte[] sha256hash1;
private byte[] sha256hash2;
private byte[] sha256hash3;
private byte[] encryptedKey0;
private byte[] encryptedKey1;
private byte[] encryptedKey2;
private byte[] encryptedKey3;
private byte[] decryptedKey0;
private byte[] decryptedKey1;
private byte[] decryptedKey2;
private byte[] decryptedKey3;
private NCAHeaderTableEntry tableEntry0;
private NCAHeaderTableEntry tableEntry1;
private NCAHeaderTableEntry tableEntry2;
private NCAHeaderTableEntry tableEntry3;
private NcaFsHeader sectionBlock0;
private NcaFsHeader sectionBlock1;
private NcaFsHeader sectionBlock2;
private NcaFsHeader sectionBlock3;
private NCAContent ncaContent0;
private NCAContent ncaContent1;
private NCAContent ncaContent2;
private NCAContent ncaContent3;
private byte[] sha256hash0, sha256hash1, sha256hash2, sha256hash3,
encryptedKey0, encryptedKey1, encryptedKey2, encryptedKey3,
decryptedKey0, decryptedKey1, decryptedKey2, decryptedKey3;
private NCAHeaderTableEntry tableEntry0, tableEntry1, tableEntry2, tableEntry3;
private NcaFsHeader sectionBlock0, sectionBlock1, sectionBlock2, sectionBlock3;
private NCAContent ncaContent0, ncaContent1, ncaContent2, ncaContent3;
public NCAProvider(File file, HashMap<String, String> keys) throws Exception{
this(file, keys, 0);
}
public NCAProvider (File file, HashMap<String, String> keys, long offsetPosition) throws Exception{
public NCAProvider(File file, HashMap<String, String> keys, long offsetPosition) throws Exception{
this.file = file;
this.keys = keys;
String header_key = keys.get("header_key");
@ -128,11 +106,11 @@ public class NCAProvider {
System.arraycopy(decryptedSequence, 0, decryptedHeader, i * 0x200, 0x200);
}
getHeader(decryptedHeader);
setupHeader(decryptedHeader);
raf.close();
getNCAContent();
setupNCAContent();
/*//---------------------------------------------------------------------
FileInputStream fis = new FileInputStream(file);
try (BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.get("/tmp/decrypted.nca")))){
@ -159,16 +137,16 @@ public class NCAProvider {
}
return data;
}
private void getHeader(byte[] decryptedData) throws Exception{
private void setupHeader(byte[] decryptedData) throws Exception{
rsa2048one = Arrays.copyOfRange(decryptedData, 0, 0x100);
rsa2048two = Arrays.copyOfRange(decryptedData, 0x100, 0x200);
magicnum = new String(decryptedData, 0x200, 0x4, StandardCharsets.US_ASCII);
magicNumber = new String(decryptedData, 0x200, 0x4, StandardCharsets.US_ASCII);
systemOrGcIndicator = decryptedData[0x204];
contentType = decryptedData[0x205];
cryptoType1 = decryptedData[0x206];
keyIndex = decryptedData[0x207];
ncaSize = getLElong(decryptedData, 0x208);
titleId = Arrays.copyOfRange(decryptedData, 0x210, 0x218);
titleId = Converter.flip(Arrays.copyOfRange(decryptedData, 0x210, 0x218));
contentIndx = Arrays.copyOfRange(decryptedData, 0x218, 0x21C);
sdkVersion = Arrays.copyOfRange(decryptedData, 0x21c, 0x220);
cryptoType2 = decryptedData[0x220];
@ -198,8 +176,8 @@ public class NCAProvider {
cryptoTypeReal -= 1;
//If nca3 proceed
if (! magicnum.equalsIgnoreCase("NCA3"))
throw new Exception("Not supported data type: "+magicnum+". Only NCA3 supported");
if (! magicNumber.equalsIgnoreCase("NCA3"))
throw new Exception("Not supported data type: "+ magicNumber +". Only NCA3 supported");
// Decrypt keys if encrypted
if (Arrays.equals(rightsId, new byte[0x10])) {
String keyAreaKey;
@ -262,35 +240,34 @@ public class NCAProvider {
throw new Exception(exceptionStringBuilder.toString());
}
private void getNCAContent() throws Exception{
byte[] key;
// If empty Rights ID
if (Arrays.equals(rightsId, new byte[0x10])) {
key = decryptedKey2; // NOTE: Just remember this dumb hack
}
else {
try {
byte[] rightsIDkey = hexStrToByteArray(keys.get(byteArrToHexString(rightsId))); // throws NullPointerException
SecretKeySpec skSpec = new SecretKeySpec(
hexStrToByteArray(keys.get(String.format("titlekek_%02x", cryptoTypeReal))
), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, skSpec);
key = cipher.doFinal(rightsIDkey);
}
catch (Exception e){
throw new Exception("No title.keys loaded for '"+
String.format("titlekek_%02x", cryptoTypeReal)+"' or '"+byteArrToHexString(rightsId)+"'? ("+e+")", e);
}
}
getNcaContentByNumber(0, key);
getNcaContentByNumber(1, key);
getNcaContentByNumber(2, key);
getNcaContentByNumber(3, key);
private void setupNCAContent() throws Exception{
byte[] key = calculateKey();
setupNcaContentByNumber(0, key);
setupNcaContentByNumber(1, key);
setupNcaContentByNumber(2, key);
setupNcaContentByNumber(3, key);
}
private void getNcaContentByNumber(int number, byte[] key){
private byte[] calculateKey() throws Exception{
try {
if (Arrays.equals(rightsId, new byte[0x10])) // If empty Rights ID
return decryptedKey2; // NOTE: Just remember this dumb hack
byte[] rightsIdKey = hexStrToByteArray(keys.get(byteArrToHexString(rightsId))); // throws NullPointerException
SecretKeySpec skSpec = new SecretKeySpec(
hexStrToByteArray(keys.get(String.format("titlekek_%02x", cryptoTypeReal))
), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, skSpec);
return cipher.doFinal(rightsIdKey);
}
catch (Exception e){
throw new Exception("No title.keys loaded for '"+
String.format("titlekek_%02x", cryptoTypeReal)+"' or '"+byteArrToHexString(rightsId)+"'? ("+e+")", e);
}
}
private void setupNcaContentByNumber(int number, byte[] key){
try {
switch (number) {
case 0:
@ -313,9 +290,11 @@ public class NCAProvider {
}
}
// -======================= API =======================-
public byte[] getRsa2048one() { return rsa2048one; }
public byte[] getRsa2048two() { return rsa2048two; }
public String getMagicnum() { return magicnum; }
public String getMagicnum() { return magicNumber; }
public byte getSystemOrGcIndicator() { return systemOrGcIndicator; }
public byte getContentType() { return contentType; }
public byte getCryptoType1() { return cryptoType1; }
@ -345,7 +324,7 @@ public class NCAProvider {
public byte[] getDecryptedKey3() { return decryptedKey3; }
/**
* Get NCA Hedaer Table Entry for selected id
* @param id should be 0-3
* @param id must be 0-3
* */
public NCAHeaderTableEntry getTableEntry(int id) throws Exception{
switch (id) {
@ -367,7 +346,7 @@ public class NCAProvider {
public NCAHeaderTableEntry getTableEntry3() { return tableEntry3; }
/**
* Get NCA Section Block for selected section
* @param id should be 0-3
* @param id must be 0-3
* */
public NcaFsHeader getSectionBlock(int id) throws Exception{
switch (id) {
@ -396,7 +375,7 @@ public class NCAProvider {
}
/**
* Get content for the selected section
* @param sectionNumber should be 0-3
* @param sectionNumber must be 0-3
* */
public NCAContent getNCAContentProvider(int sectionNumber) throws Exception{
switch (sectionNumber) {

View File

@ -20,7 +20,6 @@
package libKonogonka.Tools.RomFs;
import libKonogonka.Converter;
import libKonogonka.Tools.NCA.NCAContent;
import libKonogonka.Tools.RomFs.view.FileSystemTreeViewMaker;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -32,7 +31,7 @@ import java.util.Comparator;
import java.util.List;
public class FileSystemEntry {
private final static Logger log = LogManager.getLogger(NCAContent.class);
private final static Logger log = LogManager.getLogger(FileSystemEntry.class);
private boolean directoryFlag;
private String name;
@ -137,7 +136,7 @@ public class FileSystemEntry {
nextHashTableBucketDirectoryOffset = Converter.getLEint(dirsMetadataTable, i);
/*
if (nextHashTableBucketDirectoryOffset < 0) {
System.out.println("nextHashTableBucketDirectoryOffset: "+ nextHashTableBucketDirectoryOffset);
log.debug("nextHashTableBucketDirectoryOffset: "+ nextHashTableBucketDirectoryOffset);
}
//*/
i += 4;
@ -149,7 +148,7 @@ public class FileSystemEntry {
}
else {
dirName = "";
System.out.println("dirName: "+dirNameLength);
// log.debug("Dir Name Length: "+dirNameLength);
}
//i += getRealNameSize(dirNameLength);
}
@ -197,12 +196,12 @@ public class FileSystemEntry {
fileName = new String(Arrays.copyOfRange(filesMetadataTable, i, i + fileNameLength), StandardCharsets.UTF_8);
}
catch (Exception e){
System.out.println("fileName sizes are: "+filesMetadataTable.length+"\t"+i+"\t"+i + fileNameLength+"\t\t"+nextHashTableBucketFileOffset);
log.debug("fileName sizes are: "+filesMetadataTable.length+"\t"+i+"\t"+i + fileNameLength+"\t\t"+nextHashTableBucketFileOffset);
}
}
else {
fileName = "";
System.out.println("fileName: "+fileNameLength);
//log.debug("File Name Length: "+fileNameLength);
}
//i += getRealNameSize(fileNameLength);
}
@ -214,5 +213,4 @@ public class FileSystemEntry {
public void printTreeForDebug(){
log.debug(FileSystemTreeViewMaker.make(content, 100));
}
}

View File

@ -21,15 +21,13 @@ package libKonogonka.Tools.RomFs;
import libKonogonka.Converter;
import libKonogonka.ctraes.AesCtrBufferedInputStream;
import libKonogonka.ctraes.AesCtrDecryptSimple;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.BufferedInputStream;
import java.io.File;
import java.nio.file.Files;
public class RomFsConstruct {
private final static Logger log = LogManager.getLogger(RomFsConstruct.class);
//private final static Logger log = LogManager.getLogger(RomFsConstruct.class);
private Level6Header header;
@ -66,8 +64,6 @@ public class RomFsConstruct {
this.file = file;
this.level6Offset = level6Offset;
this.offsetPositionInFile = ncaOffset + (mediaStartOffset * 0x200);
// In 512-blocks
// In 512-blocks
this.stream = new AesCtrBufferedInputStream(
decryptor,
ncaOffset,
@ -141,7 +137,12 @@ public class RomFsConstruct {
}
private void constructRootFilesystemEntry() throws Exception{
rootEntry = new FileSystemEntry(directoryMetadataTable, fileMetadataTable);
try {
rootEntry = new FileSystemEntry(directoryMetadataTable, fileMetadataTable);
}
catch (Exception e){
throw new Exception("File: " + file.getName(), e);
}
}
private void skipBytes(long size) throws Exception{

View File

@ -23,7 +23,8 @@ rootLogger.appenderRef.stdout.ref = consoleLogger
appender.console.type = Console
appender.console.name = consoleLogger
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
#appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
appender.console.layout.pattern = %d{HH:mm:ss.SSS} libKonogonka %-5p %c{1}:%L - %m%n
##################################################
# # Enable log to files
# rootLogger.appenderRef.rolling.ref = fileLogger

View File

@ -0,0 +1,222 @@
/*
Copyright 2018-2022 Dmitry Isaenko
This file is part of libKonogonka.
libKonogonka is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
libKonogonka is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with libKonogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package libKonogonka.RomFsDecrypted;
import libKonogonka.KeyChainHolder;
import libKonogonka.RainbowDump;
import libKonogonka.TitleKeyChainHolder;
import libKonogonka.Tools.NCA.NCAProvider;
import libKonogonka.Tools.PFS0.PFS0Provider;
import libKonogonka.Tools.PFS0.PFS0subFile;
import libKonogonka.ctraes.AesCtrBufferedInputStream;
import libKonogonka.ctraes.AesCtrDecryptSimple;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.*;
import java.nio.file.Files;
import java.util.HashMap;
public class NSPpfs0EncryptedTest {
private static final String keysFileLocation = "./FilesForTests/prod.keys";
private static final String titleFileLocation = "./FilesForTests/simple_nsp.title_key";
private static final String xci_header_keyFileLocation = "./FilesForTests/xci_header_key.txt";
private static final String nspFileLocation = "./FilesForTests/sample.nsp";
private static KeyChainHolder keyChainHolder;
private static PFS0Provider pfs0Provider;
private static NCAProvider ncaProvider;
@Disabled
@DisplayName("NSP PFS0 Encrypted test")
@Test
void pfs0test() throws Exception{
BufferedReader br = new BufferedReader(new FileReader(xci_header_keyFileLocation));
String keyValue = br.readLine();
br.close();
if (keyValue == null)
throw new Exception("Unable to retrieve xci_header_key");
keyValue = keyValue.trim();
keyChainHolder = new KeyChainHolder(keysFileLocation, keyValue);
TitleKeyChainHolder titleKeyChainHolder = new TitleKeyChainHolder(titleFileLocation);
HashMap<String, String> finalKeysSet = keyChainHolder.getRawKeySet();
finalKeysSet.putAll(titleKeyChainHolder.getKeySet());
/*
for (Map.Entry e: titleKeyChainHolder.getKeySet().entrySet()){
System.out.println(e.getKey()+" = "+e.getValue());
}
for (Map.Entry e: finalKeysSet.entrySet()){
System.out.println(e.getKey()+" = "+e.getValue());
}
*/
File nspFile = new File(nspFileLocation);
pfs0Provider = new PFS0Provider(nspFile);
for (PFS0subFile subFile : pfs0Provider.getPfs0subFiles()) {
if (subFile.getName().endsWith("890.nca")) {
System.out.println("File found: "+subFile.getName());
ncaProvider = new NCAProvider(nspFile, finalKeysSet, pfs0Provider.getRawFileDataStart()+subFile.getOffset());
break;
}
}
//ncaProvider = new NCAProvider(new File(ncaFileLocation), keyChainHolder.getRawKeySet());
pfs0Validation();
AesCtrBufferedInputStreamTest();
}
void pfs0Validation() throws Exception{
for (byte i = 0; i < 4; i++){
System.out.println("..:: TEST SECTION #"+i+" ::..");
if (ncaProvider.getSectionBlock(i).getFsType() == 1 &&
ncaProvider.getSectionBlock(i).getHashType() == 2 &&
ncaProvider.getSectionBlock(i).getCryptoType() == 3){
//ncaProvider.getNCAContentProvider(i).getPfs0().printDebug();
//ncaProvider.getSectionBlock(i).printDebug();
return;
}
}
}
private AesCtrDecryptSimple decryptSimple;
long ACBISoffsetPosition;
long ACBISmediaStartOffset;
long ACBISmediaEndOffset;
long offsetPosition;
void AesCtrBufferedInputStreamTest() throws Exception {
File nca = new File(nspFileLocation); // TODO:NOTICE
PFS0subFile[] subfiles = ncaProvider.getNCAContentProvider(0).getPfs0().getPfs0subFiles();
offsetPosition = ncaProvider.getTableEntry0().getMediaStartOffset()*0x200 +
ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart();
System.out.println("\t=============================================================");
System.out.println("\tNCA SIZE: "+ RainbowDump.formatDecHexString(nca.length()));
System.out.println("\tPFS0 Offset(get) "+RainbowDump.formatDecHexString(ncaProvider.getSectionBlock0().getSuperBlockPFS0().getPfs0offset()));
System.out.println("\tPFS0 MediaStart (* 0x200) "+RainbowDump.formatDecHexString(ncaProvider.getTableEntry0().getMediaStartOffset()*0x200));
System.out.println("\tPFS0 MediaEnd (* 0x200) "+RainbowDump.formatDecHexString(ncaProvider.getTableEntry0().getMediaEndOffset()*0x200));
System.out.println("\tPFS0 Offset+MediaBlockStart: "+RainbowDump.formatDecHexString(offsetPosition));
System.out.println("\tRAW Offset: "+RainbowDump.formatDecHexString(ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart()));
System.out.println("\tHashTableSize: "+RainbowDump.formatDecHexString(ncaProvider.getSectionBlock0().getSuperBlockPFS0().getHashTableSize()));
for (PFS0subFile subFile : subfiles){
System.out.println("\n\tEntry Name: "+subFile.getName());
System.out.println("\tEntry Offset: "+RainbowDump.formatDecHexString(subFile.getOffset()));
System.out.println("\tEntry Size: "+RainbowDump.formatDecHexString(subFile.getSize()));
}
System.out.println("\t=============================================================");
ACBISoffsetPosition = 0;
ACBISmediaStartOffset = ncaProvider.getTableEntry0().getMediaStartOffset();
ACBISmediaEndOffset = ncaProvider.getTableEntry0().getMediaEndOffset();
/*
decryptSimple = new AesCtrDecryptSimple(
ncaProvider.getDecryptedKey2(),
ncaProvider.getSectionBlock0().getSectionCTR(),
ncaProvider.getTableEntry0().getMediaStartOffset()*0x200);
for (PFS0subFile subFile : subfiles){
exportContentLegacy(subFile, "/tmp/legacy_NSP_PFS0");
}
*/
//----------------------------------------------------------------------
for (PFS0subFile subFile : subfiles) {
pfs0Provider.exportContent("/tmp/2_brandnew_NSP_PFS0", subFile.getName());
}
}
private void exportContent(PFS0subFile entry, String saveToLocation) throws Exception{
File contentFile = new File(saveToLocation + entry.getName());
BufferedOutputStream extractedFileBOS = new BufferedOutputStream(Files.newOutputStream(contentFile.toPath()));
//---
InputStream is = Files.newInputStream(new File(nspFileLocation).toPath()); //TODO: NOTICE
AesCtrBufferedInputStream aesCtrBufferedInputStream = new AesCtrBufferedInputStream(
decryptSimple,
ACBISoffsetPosition,
ACBISmediaStartOffset,
ACBISmediaEndOffset,
is);
//long offsetToSkip = entry.getOffset() + ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart();
long offsetToSkip = offsetPosition+entry.getOffset();
System.out.println("\nOffsets"+
"\nRAW: "+ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart()+
"\nPfs0 offset: "+offsetPosition+
"\nentry.getOffset(): "+entry.getOffset()+
"\n");
if (offsetToSkip != aesCtrBufferedInputStream.skip(offsetToSkip))
throw new Exception("Can't skip "+
ncaProvider.getSectionBlock0().getSuperBlockPFS0().getPfs0offset()+
"("+entry.getOffset()+
" + "+
ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart()+")");
int blockSize = 0x200;
if (entry.getSize() < 0x200)
blockSize = (int) entry.getSize();
long i = 0;
byte[] block = new byte[blockSize];
int actuallyRead;
while (true) {
if ((actuallyRead = aesCtrBufferedInputStream.read(block)) != blockSize)
throw new Exception("Read failure. Block Size: "+blockSize+", actuallyRead: "+actuallyRead);
extractedFileBOS.write(block);
i += blockSize;
if ((i + blockSize) > entry.getSize()) {
blockSize = (int) (entry.getSize() - i);
if (blockSize == 0)
break;
block = new byte[blockSize];
}
}
//---
extractedFileBOS.close();
}
private void exportContentLegacy(PFS0subFile entry, String saveToLocation) throws Exception {
File contentFile = new File(saveToLocation + entry.getName());
BufferedOutputStream extractedFileBOS = new BufferedOutputStream(new FileOutputStream(contentFile));
PipedInputStream pis = ncaProvider.getNCAContentProvider(0).getPfs0().getProviderSubFilePipedInpStream(entry.getName());
byte[] readBuf = new byte[0x200]; // 8mb NOTE: consider switching to 1mb 1048576
int readSize;
while ((readSize = pis.read(readBuf)) > -1) {
extractedFileBOS.write(readBuf, 0, readSize);
readBuf = new byte[0x200];
}
extractedFileBOS.close();
}
}

View File

@ -21,8 +21,8 @@ package libKonogonka.RomFsDecrypted;
import libKonogonka.KeyChainHolder;
import libKonogonka.RainbowDump;
import libKonogonka.Tools.NCA.NCAProvider;
import libKonogonka.Tools.PFS0.IPFS0Provider;
import libKonogonka.Tools.PFS0.PFS0subFile;
import libKonogonka.Tools.RomFs.FileSystemEntry;
import libKonogonka.ctraes.AesCtrBufferedInputStream;
import libKonogonka.ctraes.AesCtrDecryptSimple;
import org.junit.jupiter.api.*;
@ -107,13 +107,17 @@ public class Pfs0EncryptedTest {
ncaProvider.getDecryptedKey2(),
ncaProvider.getSectionBlock0().getSectionCTR(),
ncaProvider.getTableEntry0().getMediaStartOffset()*0x200);
/*
for (PFS0subFile subFile : subfiles){
exportContentLegacy(subFile, "/tmp/legacy_PFS0");
}
*/
IPFS0Provider pfs0Provider = ncaProvider.getNCAContentProvider(0).getPfs0();
//----------------------------------------------------------------------
for (PFS0subFile subFile : subfiles) {
exportContent(subFile, "/tmp/brandnew_PFS0");
System.out.println("Exporting "+subFile.getName());
System.out.println("Result: "+pfs0Provider.exportContent("/tmp/1_brandnew_PFS0", subFile.getName()));
}
}

View File

@ -18,6 +18,10 @@
*/
package libKonogonka.RomFsDecrypted;
import libKonogonka.KeyChainHolder;
import libKonogonka.Tools.NCA.NCAProvider;
import libKonogonka.Tools.XCI.HFS0File;
import libKonogonka.Tools.XCI.HFS0Provider;
import libKonogonka.Tools.XCI.XCIProvider;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
@ -30,19 +34,28 @@ import java.io.FileReader;
// log.fatal("Configuration File Defined To Be :: "+System.getProperty("log4j.configurationFile"));
public class XciTest {
private static final String keysFileLocation = "./FilesForTests/prod.keys";
private static final String xci_header_keyFileLocation = "./FilesForTests/xci_header_key.txt";
private static final String decryptedFileAbsolutePath = "./FilesForTests/sample.xci";
private File xciFile;
XCIProvider provider;
String xci_header_key;
HFS0Provider hfs0Provider;
private static KeyChainHolder keyChainHolder;
@Disabled
@DisplayName("RomFsDecryptedProvider: tests")
@DisplayName("XciTest")
@Test
void romFsValidation() throws Exception{
makeFile();
getXciHeaderKey();
keyChainHolder = new KeyChainHolder(keysFileLocation, xci_header_key);
makeProvider();
getHfsSecure();
getFirstNca3();
}
void getXciHeaderKey() throws Exception{
@ -63,4 +76,20 @@ public class XciTest {
void makeProvider() throws Exception{
provider = new XCIProvider(xciFile, xci_header_key);
}
void getHfsSecure(){
hfs0Provider = provider.getHfs0ProviderSecure();
}
void getFirstNca3() throws Exception{
HFS0File hfs0File = hfs0Provider.getHfs0Files()[0];
System.out.println(hfs0File.getName() +" "+ hfs0Provider.getRawFileDataStart()+" "+hfs0File.getOffset()+ " "
+(hfs0Provider.getRawFileDataStart() + hfs0File.getOffset()));
NCAProvider ncaProvider = new NCAProvider(xciFile, keyChainHolder.getRawKeySet(),
hfs0Provider.getRawFileDataStart() +
hfs0File.getOffset());
//ncaProvider.
}
}