From 53af386738cc097f3d715d3adda1ec8d0e11481b Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Sat, 10 Dec 2022 00:26:21 +0300 Subject: [PATCH] Rework NSO0 related classes. Added option to pick one of the ELF's sections (.text, .data or .rodata) unpacked without pre-dumping it on disk. Clean 'System.out.print*' --- README.md | 12 +- misc/logo.svg | 78 ++++++-- pom.xml | 13 +- src/main/java/libKonogonka/RainbowDump.java | 39 ++-- .../NPDM/KernelAccessControlProvider.java | 43 +++-- .../libKonogonka/Tools/NSO/NSO0Header.java | 174 ++++++++++++++++++ .../libKonogonka/Tools/NSO/NSO0Provider.java | 153 ++------------- .../java/libKonogonka/Tools/NSO/NSO0Raw.java | 47 +++++ .../libKonogonka/Tools/NSO/NSO0Unpacker.java | 115 ++++++------ .../Tools/RomFs/FileSystemEntry.java | 2 +- .../libKonogonka/Tools/XCI/HFS0Provider.java | 10 +- .../Tools/XCI/XCIGamecardCert.java | 45 +++-- .../Tools/XCI/XCIGamecardInfo.java | 11 +- .../ctraes/AesCtrBufferedInputStream.java | 2 +- .../libKonogonka/RomFsDecrypted/NSOTest.java | 2 +- 15 files changed, 462 insertions(+), 284 deletions(-) create mode 100644 src/main/java/libKonogonka/Tools/NSO/NSO0Header.java create mode 100644 src/main/java/libKonogonka/Tools/NSO/NSO0Raw.java diff --git a/README.md b/README.md index 2d269d1..876eeff 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![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 source location](https://git.redrise.ru/desu/konogonka)) +Library made to work with NS-specific files / filesystem images. Separated backend of [konogonka](https://github.com/developersu/konogonka) ([independent source location](https://git.redrise.ru/desu/konogonka)) ### Let's stay in touch @@ -20,8 +20,8 @@ You can get this application from independent source location: [https://git.redr #### Thanks -* Switch brew wiki -* Original ScriesM software +* [Switch brew](https://switchbrew.org) wiki +* Original [ScriesM software](https://github.com/SciresM/) * roothorick, [shchmue](https://github.com/shchmue/), He, other Team AtlasNX discord members for their advices, notes and examples! ### System requirements @@ -30,4 +30,8 @@ JRE/JDK 8u60 or higher. ### Build -See .drone.yml \ No newline at end of file +See .drone.yml + +### Install on local host (local maven repo) + +`# mvn instal` \ No newline at end of file diff --git a/misc/logo.svg b/misc/logo.svg index dfff516..d1f8abd 100644 --- a/misc/logo.svg +++ b/misc/logo.svg @@ -25,13 +25,13 @@ inkscape:document-units="mm" showgrid="false" inkscape:zoom="2.8284272" - inkscape:cx="378.30212" - inkscape:cy="70.5339" - inkscape:window-width="2266" - inkscape:window-height="1414" - inkscape:window-x="1305" - inkscape:window-y="546" - inkscape:window-maximized="0" + inkscape:cx="378.65567" + inkscape:cy="71.06423" + inkscape:window-width="3754" + inkscape:window-height="2127" + inkscape:window-x="1166" + inkscape:window-y="0" + inkscape:window-maximized="1" inkscape:current-layer="layer1" showguides="false" /> - libKonogonka + + + + + + + + + + + + + + 2.19.0 compile + + + org.lz4 + lz4-pure-java + 1.8.0 + compile + org.junit.jupiter @@ -89,11 +96,7 @@ 5.9.0 test - - org.lz4 - lz4-pure-java - 1.8.0 - + ${project.artifactId}-${project.version}-${maven.build.timestamp} diff --git a/src/main/java/libKonogonka/RainbowDump.java b/src/main/java/libKonogonka/RainbowDump.java index dba4ffb..a2e8bd3 100644 --- a/src/main/java/libKonogonka/RainbowDump.java +++ b/src/main/java/libKonogonka/RainbowDump.java @@ -22,7 +22,11 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.nio.charset.StandardCharsets; - +/* ANSI_BLACK = "\u001B[30m"; + ANSI_YELLOW = "\u001B[33m"; + ANSI_PURPLE = "\u001B[35m"; + ANSI_CYAN = "\u001B[36m"; + ANSI_WHITE = "\u001B[37m"; */ /** * Debug tool like hexdump <3 */ @@ -30,14 +34,9 @@ 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"; private static final String ANSI_GREEN = "\u001B[32m"; - private static final String ANSI_YELLOW = "\u001B[33m"; private static final String ANSI_BLUE = "\u001B[34m"; - private static final String ANSI_PURPLE = "\u001B[35m"; - 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){ @@ -80,7 +79,13 @@ public class RainbowDump { } private static void printChars(byte[] byteArray, int pointer){ - for (int j = pointer-16; j < pointer; j++){ + int j; + if (pointer < 16) + j = 0; + else + j = pointer-16; + + for (; j < pointer; j++){ if ((byteArray[j] > 21) && (byteArray[j] < 126)) // man ascii stringBuilder.append((char) byteArray[j]); else if (byteArray[j] == 0x0a) @@ -92,18 +97,22 @@ public class RainbowDump { } } - public static void hexDumpUTF8Legacy(byte[] byteArray){ + StringBuilder stringBuilderLegacy = new StringBuilder("HexDumpUTF8Legacy"); + stringBuilderLegacy.append(ANSI_BLUE); + if (byteArray == null || byteArray.length == 0) return; - System.out.print(ANSI_BLUE); + for (int i=0; i < byteArray.length; i++) - System.out.printf("%02d-", i%100); - System.out.println(">"+ANSI_RED+byteArray.length+ANSI_RESET); + stringBuilderLegacy.append(String.format("%02d-", i%100)); + stringBuilderLegacy.append(">" + ANSI_RED).append(byteArray.length).append(ANSI_RESET).append("\n"); for (byte b: byteArray) - System.out.printf("%02x ", b); - System.out.println(); - System.out.print(new String(byteArray, StandardCharsets.UTF_8)+"\n"); + stringBuilderLegacy.append(String.format("%02x ", b)); + stringBuilderLegacy.append("\n") + .append(new String(byteArray, StandardCharsets.UTF_8)) + .append("\n"); + log.debug(stringBuilderLegacy.toString()); } public static void binDumpInt(int value){ @@ -111,7 +120,7 @@ public class RainbowDump { } public static void binDumpLong(long value){ - System.out.println(String.format("%64s", Long.toBinaryString( value )).replace(' ', '0')+" | "+value); + log.debug(String.format("%64s", Long.toBinaryString( value )).replace(' ', '0')+" | "+value); } public static String formatDecHexString(long value){ diff --git a/src/main/java/libKonogonka/Tools/NPDM/KernelAccessControlProvider.java b/src/main/java/libKonogonka/Tools/NPDM/KernelAccessControlProvider.java index cbe7f37..2f3cb97 100644 --- a/src/main/java/libKonogonka/Tools/NPDM/KernelAccessControlProvider.java +++ b/src/main/java/libKonogonka/Tools/NPDM/KernelAccessControlProvider.java @@ -19,6 +19,8 @@ package libKonogonka.Tools.NPDM; import libKonogonka.Converter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.util.LinkedHashMap; import java.util.LinkedList; @@ -73,6 +75,7 @@ for (byte i = 0; i < 16; i++){ */ public class KernelAccessControlProvider { + private final static Logger log = LogManager.getLogger(KernelAccessControlProvider.class); private static final int KERNELFLAGS = 3, SYSCALLMASK = 4, @@ -85,7 +88,7 @@ public class KernelAccessControlProvider { DEBUGFLAGS = 16; // RAW data - private LinkedList rawData; + private final LinkedList rawData; // Kernel flags private boolean kernelFlagsAvailable; private int kernelFlagCpuIdHi, @@ -93,13 +96,13 @@ public class KernelAccessControlProvider { kernelFlagThreadPrioHi, kernelFlagThreadPrioLo; // Syscall Masks as index | mask - order AS IS. [0] = bit5; [1] = bit6 - private LinkedHashMap syscallMasks; // Index, Mask + private final LinkedHashMap syscallMasks; // Index, Mask // MapIoOrNormalRange - private LinkedHashMap mapIoOrNormalRange; // alt page+num, RO flag + private final LinkedHashMap mapIoOrNormalRange; // alt page+num, RO flag // MapNormalPage (RW) private byte[] mapNormalPage; // TODO: clarify is possible to have multiple // InterruptPair - private LinkedHashMap interruptPairs; // Number; irq0, irq2 + private final LinkedHashMap interruptPairs; // Number; irq0, irq2 // Application type private int applicationType; // KernelReleaseVersion @@ -142,42 +145,42 @@ public class KernelAccessControlProvider { kernelFlagCpuIdLo = block >> 16 & 0b11111111; kernelFlagThreadPrioHi = block >> 10 & 0b111111; kernelFlagThreadPrioLo = block >> 4 & 0b111111; - //System.out.println("KERNELFLAGS "+kernelFlagCpuIdHi+" "+kernelFlagCpuIdLo+" "+kernelFlagThreadPrioHi+" "+kernelFlagThreadPrioLo); + //log.debug("KERNELFLAGS "+kernelFlagCpuIdHi+" "+kernelFlagCpuIdLo+" "+kernelFlagThreadPrioHi+" "+kernelFlagThreadPrioLo); break; case SYSCALLMASK: byte maskTableIndex = (byte) (block >> 29 & 0b111); // declared as byte; max value could be 7; min - 0; byte[] mask = new byte[24]; // Consider as bit. - //System.out.println("SYSCALLMASK ind: "+maskTableIndex); + //log.debug("SYSCALLMASK ind: "+maskTableIndex); for (int k = 28; k >= 5; k--) { mask[k-5] = (byte) (block >> k & 1); // Only 1 or 0 possible - //System.out.print(mask[k-5]); + //log.debug(" " + mask[k-5]); } - //System.out.println(); + //log.debug(); syscallMasks.put(maskTableIndex, mask); break; case MAPIOORNORMALRANGE: byte[] altStPgNPgNum = new byte[24]; - //System.out.println("MAPIOORNORMALRANGE Flag: "+((block >> 31 & 1) != 0)); + //log.debug("MAPIOORNORMALRANGE Flag: "+((block >> 31 & 1) != 0)); for (int k = 30; k >= 7; k--){ altStPgNPgNum[k-7] = (byte) (block >> k & 1); // Only 1 or 0 possible - //System.out.print(altStPgNPgNum[k-7]); + //log.debug(" " + altStPgNPgNum[k-7]); } mapIoOrNormalRange.put(altStPgNPgNum, (block >> 31 & 1) != 0); - //System.out.println(); + //log.debug(); break; case MAPNORMALPAGE_RW: - //System.out.println("MAPNORMALPAGE_RW\t"); + //log.debug("MAPNORMALPAGE_RW\t"); mapNormalPage = new byte[24]; for (int k = 31; k >= 8; k--){ mapNormalPage[k-8] = (byte) (block >> k & 1); - //System.out.print(mapNormalPage[k-8]); + //log.debug(" " + mapNormalPage[k-8]); } - //System.out.println(); + //log.debug(); break; case INTERRUPTPAIR: - //System.out.println("INTERRUPTPAIR"); + //log.debug("INTERRUPTPAIR"); //RainbowHexDump.octDumpInt(block); byte[][] pair = new byte[2][]; byte[] irq0 = new byte[10]; @@ -193,26 +196,26 @@ public class KernelAccessControlProvider { break; case APPLICATIONTYPE: applicationType = block >> 14 & 0b111; - //System.out.println("APPLICATIONTYPE "+applicationType); + //log.debug("APPLICATIONTYPE "+applicationType); break; case KERNELRELEASEVERSION: - //System.out.println("KERNELRELEASEVERSION\t"+(block >> 19 & 0b111111111111)+"."+(block >> 15 & 0b1111)+".X"); + //log.debug("KERNELRELEASEVERSION\t"+(block >> 19 & 0b111111111111)+"."+(block >> 15 & 0b1111)+".X"); isKernelRelVersionAvailable = true; kernelRelVersionMajor = (block >> 19 & 0b111111111111); kernelRelVersionMinor = (block >> 15 & 0b1111); break; case HANDLETABLESIZE: handleTableSize = block >> 16 & 0b1111111111; - //System.out.println("HANDLETABLESIZE "+handleTableSize); + //log.debug("HANDLETABLESIZE "+handleTableSize); break; case DEBUGFLAGS: debugFlagsAvailable = true; canBeDebugged = (block >> 17 & 1) != 0; canDebugOthers = (block >> 18 & 1) != 0; - //System.out.println("DEBUGFLAGS "+canBeDebugged+" "+canDebugOthers); + //log.debug("DEBUGFLAGS "+canBeDebugged+" "+canDebugOthers); break; default: - System.out.println("UNKNOWN\t\t"+block+" "+type); + log.error("UNKNOWN\t\t"+block+" "+type); } } } diff --git a/src/main/java/libKonogonka/Tools/NSO/NSO0Header.java b/src/main/java/libKonogonka/Tools/NSO/NSO0Header.java new file mode 100644 index 0000000..dd5af64 --- /dev/null +++ b/src/main/java/libKonogonka/Tools/NSO/NSO0Header.java @@ -0,0 +1,174 @@ +/* + 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 . + */ +package libKonogonka.Tools.NSO; + +import libKonogonka.Converter; +import libKonogonka.RainbowDump; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +import static libKonogonka.Converter.getLEint; + +public class NSO0Header { + private final static Logger log = LogManager.getLogger(NSO0Header.class); + + private String magic; + private int version; + private byte[] upperReserved; + private int flags; + private SegmentHeader textSegmentHeader; + private int moduleNameOffset; // note: could be unsigned int; consider 'long' + private SegmentHeader rodataSegmentHeader; + private int moduleNameSize; + private SegmentHeader dataSegmentHeader; + private int bssSize; // note: could be unsigned int; consider 'long' + private byte[] moduleId; + private int textCompressedSize; + private int rodataCompressedSize; + private int dataCompressedSize; + private byte[] bottomReserved; + private SegmentHeaderRelative _api_infoRelative; + private SegmentHeaderRelative _dynstrRelative; + private SegmentHeaderRelative _dynsymRelative; + private byte[] textHash; + private byte[] rodataHash; + private byte[] dataHash; + + public NSO0Header(byte[] headerBytes) throws Exception{ + if (headerBytes.length < 0x100) + throw new Exception("Incorrect NSO0 header size"); + parse(headerBytes); + } + + private void parse(byte[] knownStartingBytes) throws Exception{ + this.magic = new String(knownStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII); + if (! magic.equals("NSO0")){ + throw new Exception("Bad magic"); + } + + this.version = getLEint(knownStartingBytes, 0x4); + this.upperReserved = Arrays.copyOfRange(knownStartingBytes, 0x8, 0xC); + this.flags = getLEint(knownStartingBytes, 0xC); + this.textSegmentHeader = new SegmentHeader(knownStartingBytes, 0x10); + this.moduleNameOffset = Converter.getLEint(knownStartingBytes, 0x1C); + this.rodataSegmentHeader = new SegmentHeader(knownStartingBytes, 0x20); + this.moduleNameSize = Converter.getLEint(knownStartingBytes, 0x2C); + this.dataSegmentHeader = new SegmentHeader(knownStartingBytes, 0x30); + this.bssSize = Converter.getLEint(knownStartingBytes, 0x3C); + this.moduleId = Arrays.copyOfRange(knownStartingBytes, 0x40, 0x60); + this.textCompressedSize = Converter.getLEint(knownStartingBytes, 0x60); + this.rodataCompressedSize = Converter.getLEint(knownStartingBytes, 0x64); + this.dataCompressedSize = Converter.getLEint(knownStartingBytes, 0x68); + this.bottomReserved = Arrays.copyOfRange(knownStartingBytes, 0x6C, 0x88); + this._api_infoRelative = new SegmentHeaderRelative(knownStartingBytes, 0x88); + this._dynstrRelative = new SegmentHeaderRelative(knownStartingBytes, 0x90); + this._dynsymRelative = new SegmentHeaderRelative(knownStartingBytes, 0x98); + this.textHash = Arrays.copyOfRange(knownStartingBytes, 0xA0, 0xC0); + this.rodataHash = Arrays.copyOfRange(knownStartingBytes, 0xC0, 0xE0); + this.dataHash = Arrays.copyOfRange(knownStartingBytes, 0xE0, 0x100); + } + + /* API */ + public String getMagic() { return magic; } + public int getVersion() {return version; } + public byte[] getUpperReserved() { return upperReserved; } + public int getFlags() { return flags; } + public boolean isTextCompressFlag(){ return (flags & 0b000001) == 1; } + public boolean isRoCompressFlag(){ return (flags & 0b000010) >> 1 == 1; } + public boolean isDataCompressFlag(){ return (flags & 0b000100 ) >> 2 == 1; } + public boolean isTextHashFlag(){ return (flags & 0b001000 ) >> 3 == 1; } + public boolean isRoHashFlag(){ return (flags & 0b010000 ) >> 4 == 1; } + public boolean isDataHashFlag(){ return (flags & 0b100000 ) >> 5 == 1; } + public SegmentHeader getTextSegmentHeader() { return textSegmentHeader; } + public int getModuleNameOffset() { return moduleNameOffset; } + public SegmentHeader getRodataSegmentHeader() { return rodataSegmentHeader; } + public int getModuleNameSize() { return moduleNameSize; } + public SegmentHeader getDataSegmentHeader() { return dataSegmentHeader; } + public int getBssSize() { return bssSize; } + public byte[] getModuleId() { return moduleId; } + public int getTextCompressedSize() { return textCompressedSize; } + public int getRodataCompressedSize() { return rodataCompressedSize; } + public int getDataCompressedSize() { return dataCompressedSize; } + public byte[] getBottomReserved() { return bottomReserved; } + public SegmentHeaderRelative get_api_infoRelative() { return _api_infoRelative; } + public SegmentHeaderRelative get_dynstrRelative() { return _dynstrRelative; } + public SegmentHeaderRelative get_dynsymRelative() { return _dynsymRelative; } + public byte[] getTextHash() { return textHash; } + public byte[] getRodataHash() { return rodataHash; } + public byte[] getDataHash() { return dataHash; } + + public void printDebug(){ + log.debug(".:: NSO0 Provider ::.\n" + + " ============================================================= \n" + + "Magic \"NSO0\" " + magic + "\n" + + "Version (always 0) " + version + "\n" + + "Reserved " + Converter.byteArrToHexString(upperReserved) + "\n" + + "Flags " + Converter.intToBinaryString(flags) + "\n" + + " |- 0. .text Compress " + isTextCompressFlag() + "\n" + + " |- 1. .rodata Compress " + isRoCompressFlag() + "\n" + + " |- 2. .data Compress " + isDataCompressFlag() + "\n" + + " |- 3. .text Hash " + isTextHashFlag() + "\n" + + " |- 4. .rodata Hash " + isRoHashFlag() + "\n" + + " |- 5. .data Hash " + isDataHashFlag() + "\n" + + " +++\n"+ + "SegmentHeader for .text\n" + + " |- File Offset - - - - - - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getSegmentOffset()) + "\n" + + " |- Memory Offset - - - - - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getMemoryOffset()) + "\n" + + " |- Size As Decompressed - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getSizeAsDecompressed()) + "\n" + + "ModuleNameOffset (calculated by sizeof(header)) " + RainbowDump.formatDecHexString(moduleNameOffset) + "\n" + + " +++\n"+ + "SegmentHeader for .rodata\n" + + " |- File Offset - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getSegmentOffset()) + "\n" + + " |- Memory Offset - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getMemoryOffset()) + "\n" + + " |- Size As Decompressed - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getSizeAsDecompressed()) + "\n" + + "ModuleNameSize " + RainbowDump.formatDecHexString(moduleNameSize) + "\n" + + " +++\n"+ + "SegmentHeader for .data\n" + + " |- File Offset - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getSegmentOffset()) + "\n" + + " |- Memory Offset - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getMemoryOffset()) + "\n" + + " |- Size As Decompressed - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getSizeAsDecompressed()) + "\n" + + " .bss Size " + RainbowDump.formatDecHexString(bssSize) + "\n" + // Block Started by Symbol + "Module ID (aka Build ID) " + Converter.byteArrToHexString(moduleId) + "\n" + + " .text Size (compressed) " + RainbowDump.formatDecHexString(textCompressedSize) + "\n" + + " .rodata Size (compressed) " + RainbowDump.formatDecHexString(rodataCompressedSize) + "\n" + + " .data Size (compressed) " + RainbowDump.formatDecHexString(dataCompressedSize) + "\n" + + "Reserved " + Converter.byteArrToHexString(bottomReserved) + "\n" + + " xxx\n"+ + "SegmentHeaderRelative for .api_info\n" + + " |- Offset - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_api_infoRelative.getOffset()) + "\n" + + " |- Size - - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_api_infoRelative.getSize()) + "\n" + + " xxx\n"+ + "SegmentHeaderRelative for .dynstr\n" + + " |- Offset - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynstrRelative.getOffset()) + "\n" + + " |- Size - - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynstrRelative.getSize()) + "\n" + + " xxx\n"+ + "SegmentHeaderRelative for .dynsym\n" + + " |- Offset - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynsymRelative.getOffset()) + "\n" + + " |- Size - - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynsymRelative.getSize()) + "\n" + + " xxx\n"+ + ".text decompressed' SHA-256 hash " + Converter.byteArrToHexString(textHash) + "\n" + + ".rodata decompressed' SHA-256 hash " + Converter.byteArrToHexString(rodataHash) + "\n" + + ".data decompressed' SHA-256 hash " + Converter.byteArrToHexString(dataHash) + "\n" + + " ============================================================= " + ); + } +} diff --git a/src/main/java/libKonogonka/Tools/NSO/NSO0Provider.java b/src/main/java/libKonogonka/Tools/NSO/NSO0Provider.java index 0c9fba7..9c91779 100644 --- a/src/main/java/libKonogonka/Tools/NSO/NSO0Provider.java +++ b/src/main/java/libKonogonka/Tools/NSO/NSO0Provider.java @@ -18,45 +18,15 @@ */ package libKonogonka.Tools.NSO; -import libKonogonka.Converter; -import libKonogonka.RainbowDump; import libKonogonka.ctraes.InFileStreamProducer; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import java.io.BufferedInputStream; import java.io.File; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -import static libKonogonka.Converter.getLEint; public class NSO0Provider { - private final static Logger log = LogManager.getLogger(NSO0Provider.class); - private final InFileStreamProducer producer; - private String magic; - private int version; - private byte[] upperReserved; - private int flags; - private SegmentHeader textSegmentHeader; - private int moduleNameOffset; // note: could be unsigned int; consider 'long' - private SegmentHeader rodataSegmentHeader; - private int moduleNameSize; - private SegmentHeader dataSegmentHeader; - private int bssSize; // note: could be unsigned int; consider 'long' - private byte[] moduleId; - private int textCompressedSize; - private int rodataCompressedSize; - private int dataCompressedSize; - private byte[] bottomReserved; - private SegmentHeaderRelative _api_infoRelative; - private SegmentHeaderRelative _dynstrRelative; - private SegmentHeaderRelative _dynsymRelative; - private byte[] textHash; - private byte[] rodataHash; - private byte[] dataHash; + private final NSO0Header header; /* Encrypted */ public NSO0Provider(InFileStreamProducer producer) throws Exception { @@ -65,7 +35,7 @@ public class NSO0Provider { byte[] knownStartingBytes = new byte[0x100]; if (0x100 != stream.read(knownStartingBytes)) throw new Exception("Reading stream suddenly ended while trying to read starting 0x100 bytes"); - parse(knownStartingBytes); + this.header = new NSO0Header(knownStartingBytes); } } @@ -82,123 +52,26 @@ public class NSO0Provider { byte[] knownStartingBytes = new byte[0x100]; if (0x100 != stream.read(knownStartingBytes)) throw new Exception("Reading stream suddenly ended while trying to read starting 0x100 bytes"); - parse(knownStartingBytes); + this.header = new NSO0Header(knownStartingBytes); } } - private void parse(byte[] knownStartingBytes) throws Exception{ - this.magic = new String(knownStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII); - if (! magic.equals("NSO0")){ - throw new Exception("Bad magic"); - } - this.version = getLEint(knownStartingBytes, 0x4); - this.upperReserved = Arrays.copyOfRange(knownStartingBytes, 0x8, 0xC); - this.flags = getLEint(knownStartingBytes, 0xC); - this.textSegmentHeader = new SegmentHeader(knownStartingBytes, 0x10); - this.moduleNameOffset = Converter.getLEint(knownStartingBytes, 0x1C); - this.rodataSegmentHeader = new SegmentHeader(knownStartingBytes, 0x20); - this.moduleNameSize = Converter.getLEint(knownStartingBytes, 0x2C); - this.dataSegmentHeader = new SegmentHeader(knownStartingBytes, 0x30); - this.bssSize = Converter.getLEint(knownStartingBytes, 0x3C); - this.moduleId = Arrays.copyOfRange(knownStartingBytes, 0x40, 0x60); - this.textCompressedSize = Converter.getLEint(knownStartingBytes, 0x60); - this.rodataCompressedSize = Converter.getLEint(knownStartingBytes, 0x64); - this.dataCompressedSize = Converter.getLEint(knownStartingBytes, 0x68); - this.bottomReserved = Arrays.copyOfRange(knownStartingBytes, 0x6C, 0x88); - this._api_infoRelative = new SegmentHeaderRelative(knownStartingBytes, 0x88); - this._dynstrRelative = new SegmentHeaderRelative(knownStartingBytes, 0x90); - this._dynsymRelative = new SegmentHeaderRelative(knownStartingBytes, 0x98); - this.textHash = Arrays.copyOfRange(knownStartingBytes, 0xA0, 0xC0); - this.rodataHash = Arrays.copyOfRange(knownStartingBytes, 0xC0, 0xE0); - this.dataHash = Arrays.copyOfRange(knownStartingBytes, 0xE0, 0x100); + public NSO0Header getHeader() { + return header; } public void exportAsDecompressedNSO0(String saveToLocation) throws Exception{ - NSO0Unpacker.unpack(this, producer, saveToLocation); + NSO0Unpacker.unpack(header, producer, saveToLocation); } - /* API */ - public String getMagic() { return magic; } - public int getVersion() {return version; } - public byte[] getUpperReserved() { return upperReserved; } - public int getFlags() { return flags; } - public boolean isTextCompressFlag(){ return (flags & 0b000001) == 1; } - public boolean isRoCompressFlag(){ return (flags & 0b000010) >> 1 == 1; } - public boolean isDataCompressFlag(){ return (flags & 0b000100 ) >> 2 == 1; } - public boolean isTextHashFlag(){ return (flags & 0b001000 ) >> 3 == 1; } - public boolean isRoHashFlag(){ return (flags & 0b010000 ) >> 4 == 1; } - public boolean isDataHashFlag(){ return (flags & 0b100000 ) >> 5 == 1; } - public SegmentHeader getTextSegmentHeader() { return textSegmentHeader; } - public int getModuleNameOffset() { return moduleNameOffset; } - public SegmentHeader getRodataSegmentHeader() { return rodataSegmentHeader; } - public int getModuleNameSize() { return moduleNameSize; } - public SegmentHeader getDataSegmentHeader() { return dataSegmentHeader; } - public int getBssSize() { return bssSize; } - public byte[] getModuleId() { return moduleId; } - public int getTextCompressedSize() { return textCompressedSize; } - public int getRodataCompressedSize() { return rodataCompressedSize; } - public int getDataCompressedSize() { return dataCompressedSize; } - public byte[] getBottomReserved() { return bottomReserved; } - public SegmentHeaderRelative get_api_infoRelative() { return _api_infoRelative; } - public SegmentHeaderRelative get_dynstrRelative() { return _dynstrRelative; } - public SegmentHeaderRelative get_dynsymRelative() { return _dynsymRelative; } - public byte[] getTextHash() { return textHash; } - public byte[] getRodataHash() { return rodataHash; } - public byte[] getDataHash() { return dataHash; } + public NSO0Raw getAsDecompressedNSO0() throws Exception{ + return NSO0Unpacker.getNSO0Raw(header, producer); + } + /** + * Prints header.printDebug() + * */ public void printDebug(){ - log.debug(".:: NSO0 Provider ::.\n" + - " ============================================================= \n" + - "Magic \"NSO0\" " + magic + "\n" + - "Version (always 0) " + version + "\n" + - "Reserved " + Converter.byteArrToHexString(upperReserved) + "\n" + - "Flags " + Converter.intToBinaryString(flags) + "\n" + - " |- 0. .text Compress " + isTextCompressFlag() + "\n" + - " |- 1. .rodata Compress " + isRoCompressFlag() + "\n" + - " |- 2. .data Compress " + isDataCompressFlag() + "\n" + - " |- 3. .text Hash " + isTextHashFlag() + "\n" + - " |- 4. .rodata Hash " + isRoHashFlag() + "\n" + - " |- 5. .data Hash " + isDataHashFlag() + "\n" + - " +++\n"+ - "SegmentHeader for .text\n" + - " |- File Offset - - - - - - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getSegmentOffset()) + "\n" + - " |- Memory Offset - - - - - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getMemoryOffset()) + "\n" + - " |- Size As Decompressed - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getSizeAsDecompressed()) + "\n" + - "ModuleNameOffset (calculated by sizeof(header)) " + RainbowDump.formatDecHexString(moduleNameOffset) + "\n" + - " +++\n"+ - "SegmentHeader for .rodata\n" + - " |- File Offset - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getSegmentOffset()) + "\n" + - " |- Memory Offset - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getMemoryOffset()) + "\n" + - " |- Size As Decompressed - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getSizeAsDecompressed()) + "\n" + - "ModuleNameSize " + RainbowDump.formatDecHexString(moduleNameSize) + "\n" + - " +++\n"+ - "SegmentHeader for .data\n" + - " |- File Offset - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getSegmentOffset()) + "\n" + - " |- Memory Offset - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getMemoryOffset()) + "\n" + - " |- Size As Decompressed - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getSizeAsDecompressed()) + "\n" + - " .bss Size " + RainbowDump.formatDecHexString(bssSize) + "\n" + // Block Started by Symbol - "Module ID (aka Build ID) " + Converter.byteArrToHexString(moduleId) + "\n" + - " .text Size (compressed) " + RainbowDump.formatDecHexString(textCompressedSize) + "\n" + - " .rodata Size (compressed) " + RainbowDump.formatDecHexString(rodataCompressedSize) + "\n" + - " .data Size (compressed) " + RainbowDump.formatDecHexString(dataCompressedSize) + "\n" + - "Reserved " + Converter.byteArrToHexString(bottomReserved) + "\n" + - " xxx\n"+ - "SegmentHeaderRelative for .api_info\n" + - " |- Offset - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_api_infoRelative.getOffset()) + "\n" + - " |- Size - - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_api_infoRelative.getSize()) + "\n" + - " xxx\n"+ - "SegmentHeaderRelative for .dynstr\n" + - " |- Offset - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynstrRelative.getOffset()) + "\n" + - " |- Size - - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynstrRelative.getSize()) + "\n" + - " xxx\n"+ - "SegmentHeaderRelative for .dynsym\n" + - " |- Offset - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynsymRelative.getOffset()) + "\n" + - " |- Size - - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynsymRelative.getSize()) + "\n" + - " xxx\n"+ - ".text decompressed' SHA-256 hash " + Converter.byteArrToHexString(textHash) + "\n" + - ".rodata decompressed' SHA-256 hash " + Converter.byteArrToHexString(rodataHash) + "\n" + - ".data decompressed' SHA-256 hash " + Converter.byteArrToHexString(dataHash) + "\n" + - " ============================================================= " - ); + header.printDebug(); } } diff --git a/src/main/java/libKonogonka/Tools/NSO/NSO0Raw.java b/src/main/java/libKonogonka/Tools/NSO/NSO0Raw.java new file mode 100644 index 0000000..97304e8 --- /dev/null +++ b/src/main/java/libKonogonka/Tools/NSO/NSO0Raw.java @@ -0,0 +1,47 @@ +/* + 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 . + */ +package libKonogonka.Tools.NSO; + +public class NSO0Raw { + private NSO0Header headerObject; + private final byte[] header; + private final byte[] _textDecompressedSection; + private final byte[] _rodataDecompressedSection; + private final byte[] _dataDecompressedSection; + + NSO0Raw(byte[] header, + byte[] _textDecompressedSection, + byte[] _rodataDecompressedSection, + byte[] _dataDecompressedSection){ + this.header = header; + this._textDecompressedSection = _textDecompressedSection; + this. _rodataDecompressedSection = _rodataDecompressedSection; + this._dataDecompressedSection = _dataDecompressedSection; + try { + this.headerObject = new NSO0Header(header); + } + catch (Exception e){ e.printStackTrace(); } //never happens + } + + public NSO0Header getHeader() { return headerObject; } + public byte[] getHeaderRaw() {return header;} + public byte[] getTextRaw() {return _textDecompressedSection;} + public byte[] getRodataRaw() {return _rodataDecompressedSection;} + public byte[] getDataRaw() {return _dataDecompressedSection;} +} diff --git a/src/main/java/libKonogonka/Tools/NSO/NSO0Unpacker.java b/src/main/java/libKonogonka/Tools/NSO/NSO0Unpacker.java index 1e61e66..8d81cc7 100644 --- a/src/main/java/libKonogonka/Tools/NSO/NSO0Unpacker.java +++ b/src/main/java/libKonogonka/Tools/NSO/NSO0Unpacker.java @@ -29,15 +29,14 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Arrays; -public class NSO0Unpacker { +class NSO0Unpacker { private static final String DECOMPRESSED_FILE_NAME = "main_decompressed"; private final MessageDigest digest = MessageDigest.getInstance("SHA-256"); - private NSO0Provider provider; - private InFileStreamProducer producer; + private final NSO0Header nso0Header; + private final InFileStreamProducer producer; private byte[] _textDecompressedSection; private byte[] _rodataDecompressedSection; @@ -47,19 +46,29 @@ public class NSO0Unpacker { private int rodataFileOffsetNew; private int dataFileOffsetNew; - private NSO0Unpacker() throws NoSuchAlgorithmException { } + private NSO0Unpacker(NSO0Header nso0Header, InFileStreamProducer producer) throws Exception { + this.nso0Header = nso0Header; + this.producer = producer; - public static void unpack(NSO0Provider provider, InFileStreamProducer producer, String saveToLocation) throws Exception{ - if (! provider.isTextCompressFlag() && ! provider.isRoCompressFlag() && ! provider.isDataCompressFlag()) + decompressSections(); + validateHashes(); + makeHeader(); + } + + static NSO0Raw getNSO0Raw(NSO0Header nso0Header, InFileStreamProducer producer) throws Exception{ + NSO0Unpacker instance = new NSO0Unpacker(nso0Header, producer); + + return new NSO0Raw(instance.header, + instance._textDecompressedSection, + instance._rodataDecompressedSection, + instance._dataDecompressedSection); + } + + static void unpack(NSO0Header nso0Header, InFileStreamProducer producer, String saveToLocation) throws Exception{ + if (! nso0Header.isTextCompressFlag() && ! nso0Header.isRoCompressFlag() && ! nso0Header.isDataCompressFlag()) throw new Exception("This file is not compressed"); + NSO0Unpacker instance = new NSO0Unpacker(nso0Header, producer); - NSO0Unpacker instance = new NSO0Unpacker(); - instance.provider = provider; - instance.producer = producer; - - instance.decompressSections(); - instance.validateHashes(); - instance.makeHeader(); instance.writeFile(saveToLocation); } @@ -69,22 +78,22 @@ public class NSO0Unpacker { decompressDataSection(); } private void decompressTextSection() throws Exception{ - if (provider.isTextCompressFlag()) - _textDecompressedSection = decompressSection(provider.getTextSegmentHeader(), provider.getTextCompressedSize()); + if (nso0Header.isTextCompressFlag()) + _textDecompressedSection = decompressSection(nso0Header.getTextSegmentHeader(), nso0Header.getTextCompressedSize()); else - _textDecompressedSection = duplicateSection(provider.getTextSegmentHeader()); + _textDecompressedSection = duplicateSection(nso0Header.getTextSegmentHeader()); } private void decompressRodataSection() throws Exception{ - if (provider.isRoCompressFlag()) - _rodataDecompressedSection = decompressSection(provider.getRodataSegmentHeader(), provider.getRodataCompressedSize()); + if (nso0Header.isRoCompressFlag()) + _rodataDecompressedSection = decompressSection(nso0Header.getRodataSegmentHeader(), nso0Header.getRodataCompressedSize()); else - _rodataDecompressedSection = duplicateSection(provider.getRodataSegmentHeader()); + _rodataDecompressedSection = duplicateSection(nso0Header.getRodataSegmentHeader()); } private void decompressDataSection() throws Exception{ - if (provider.isDataCompressFlag()) - _dataDecompressedSection = decompressSection(provider.getDataSegmentHeader(), provider.getDataCompressedSize()); + if (nso0Header.isDataCompressFlag()) + _dataDecompressedSection = decompressSection(nso0Header.getDataSegmentHeader(), nso0Header.getDataCompressedSize()); else - _dataDecompressedSection = duplicateSection(provider.getDataSegmentHeader()); + _dataDecompressedSection = duplicateSection(nso0Header.getDataSegmentHeader()); } private byte[] decompressSection(SegmentHeader segmentHeader, int compressedSectionSize) throws Exception{ @@ -126,13 +135,13 @@ public class NSO0Unpacker { } private void validateHashes() throws Exception{ - if ( ! Arrays.equals(provider.getTextHash(), digest.digest(_textDecompressedSection)) ) { + if ( ! Arrays.equals(nso0Header.getTextHash(), digest.digest(_textDecompressedSection)) ) { throw new Exception(".text hash mismatch for .text section"); } - if ( ! Arrays.equals(provider.getRodataHash(), digest.digest(_rodataDecompressedSection)) ) { + if ( ! Arrays.equals(nso0Header.getRodataHash(), digest.digest(_rodataDecompressedSection)) ) { throw new Exception(".rodata hash mismatch for .text section"); } - if ( ! Arrays.equals(provider.getDataHash(), digest.digest(_dataDecompressedSection)) ) { + if ( ! Arrays.equals(nso0Header.getDataHash(), digest.digest(_dataDecompressedSection)) ) { throw new Exception(".data hash mismatch for .text section"); } } @@ -163,41 +172,41 @@ public class NSO0Unpacker { header = resultingHeader.array(); */ - textFileOffsetNew = provider.getTextSegmentHeader().getMemoryOffset()+0x100; - rodataFileOffsetNew = provider.getRodataSegmentHeader().getMemoryOffset()+0x100; - dataFileOffsetNew = provider.getDataSegmentHeader().getMemoryOffset()+0x100; + textFileOffsetNew = nso0Header.getTextSegmentHeader().getMemoryOffset()+0x100; + rodataFileOffsetNew = nso0Header.getRodataSegmentHeader().getMemoryOffset()+0x100; + dataFileOffsetNew = nso0Header.getDataSegmentHeader().getMemoryOffset()+0x100; ByteBuffer resultingHeader = ByteBuffer.allocate(0x100).order(ByteOrder.LITTLE_ENDIAN); resultingHeader.put("NSO0".getBytes(StandardCharsets.US_ASCII)) - .putInt(provider.getVersion()) - .put(provider.getUpperReserved()) - .putInt(provider.getFlags() & 0b111000) + .putInt(nso0Header.getVersion()) + .put(nso0Header.getUpperReserved()) + .putInt(nso0Header.getFlags() & 0b111000) .putInt(textFileOffsetNew) - .putInt(provider.getTextSegmentHeader().getMemoryOffset()) - .putInt(provider.getTextSegmentHeader().getSizeAsDecompressed()) + .putInt(nso0Header.getTextSegmentHeader().getMemoryOffset()) + .putInt(nso0Header.getTextSegmentHeader().getSizeAsDecompressed()) .putInt(0x100) .putInt(rodataFileOffsetNew) - .putInt(provider.getRodataSegmentHeader().getMemoryOffset()) - .putInt(provider.getRodataSegmentHeader().getSizeAsDecompressed()) + .putInt(nso0Header.getRodataSegmentHeader().getMemoryOffset()) + .putInt(nso0Header.getRodataSegmentHeader().getSizeAsDecompressed()) .putInt(0) .putInt(dataFileOffsetNew) - .putInt(provider.getDataSegmentHeader().getMemoryOffset()) - .putInt(provider.getDataSegmentHeader().getSizeAsDecompressed()) - .putInt(provider.getBssSize()) - .put(provider.getModuleId()) - .putInt(provider.getTextSegmentHeader().getSizeAsDecompressed()) - .putInt(provider.getRodataSegmentHeader().getSizeAsDecompressed()) - .putInt(provider.getDataSegmentHeader().getSizeAsDecompressed()) - .put(provider.getBottomReserved()) - .putInt(provider.get_api_infoRelative().getOffset()) - .putInt(provider.get_api_infoRelative().getSize()) - .putInt(provider.get_dynstrRelative().getOffset()) - .putInt(provider.get_dynstrRelative().getSize()) - .putInt(provider.get_dynsymRelative().getOffset()) - .putInt(provider.get_dynsymRelative().getSize()) - .put(provider.getTextHash()) - .put(provider.getRodataHash()) - .put(provider.getDataHash()); + .putInt(nso0Header.getDataSegmentHeader().getMemoryOffset()) + .putInt(nso0Header.getDataSegmentHeader().getSizeAsDecompressed()) + .putInt(nso0Header.getBssSize()) + .put(nso0Header.getModuleId()) + .putInt(nso0Header.getTextSegmentHeader().getSizeAsDecompressed()) + .putInt(nso0Header.getRodataSegmentHeader().getSizeAsDecompressed()) + .putInt(nso0Header.getDataSegmentHeader().getSizeAsDecompressed()) + .put(nso0Header.getBottomReserved()) + .putInt(nso0Header.get_api_infoRelative().getOffset()) + .putInt(nso0Header.get_api_infoRelative().getSize()) + .putInt(nso0Header.get_dynstrRelative().getOffset()) + .putInt(nso0Header.get_dynstrRelative().getSize()) + .putInt(nso0Header.get_dynsymRelative().getOffset()) + .putInt(nso0Header.get_dynsymRelative().getSize()) + .put(nso0Header.getTextHash()) + .put(nso0Header.getRodataHash()) + .put(nso0Header.getDataHash()); header = resultingHeader.array(); } diff --git a/src/main/java/libKonogonka/Tools/RomFs/FileSystemEntry.java b/src/main/java/libKonogonka/Tools/RomFs/FileSystemEntry.java index b2f5e14..75877c2 100644 --- a/src/main/java/libKonogonka/Tools/RomFs/FileSystemEntry.java +++ b/src/main/java/libKonogonka/Tools/RomFs/FileSystemEntry.java @@ -184,7 +184,7 @@ public class FileSystemEntry { nextHashTableBucketFileOffset = Converter.getLEint(filesMetadataTable, i); /* if (nextHashTableBucketFileOffset < 0) { - System.out.println("nextHashTableBucketFileOffset: "+ nextHashTableBucketFileOffset); + log.debug("nextHashTableBucketFileOffset: "+ nextHashTableBucketFileOffset); } //*/ i += 4; diff --git a/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java b/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java index 9713a38..44ff19e 100644 --- a/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java +++ b/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java @@ -19,6 +19,8 @@ package libKonogonka.Tools.XCI; import libKonogonka.Tools.ISuperProvider; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.io.*; import java.nio.charset.StandardCharsets; @@ -31,6 +33,8 @@ import static libKonogonka.Converter.*; * HFS0 * */ public class HFS0Provider implements ISuperProvider { + private final static Logger log = LogManager.getLogger(HFS0Provider.class); + private final String magic; private final int filesCount; private final byte[] padding; @@ -136,7 +140,7 @@ public class HFS0Provider implements ISuperProvider { PipedInputStream streamIn = new PipedInputStream(streamOut); workerThread = new Thread(() -> { - System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Executing thread"); + log.trace("HFS0Provider -> getHfs0FilePipedInpStream(): Executing thread"); try{ long subFileRealPosition = rawFileDataStart + hfs0Files[subFileNumber].getOffset(); BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath())); @@ -164,10 +168,10 @@ public class HFS0Provider implements ISuperProvider { streamOut.close(); } catch (Exception ioe){ - System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to provide stream"); + log.error("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to provide stream"); ioe.printStackTrace(); } - System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Thread died"); + log.trace("HFS0Provider -> getHfs0FilePipedInpStream(): Thread died"); }); workerThread.start(); return streamIn; diff --git a/src/main/java/libKonogonka/Tools/XCI/XCIGamecardCert.java b/src/main/java/libKonogonka/Tools/XCI/XCIGamecardCert.java index 7d5451f..4bcfa76 100644 --- a/src/main/java/libKonogonka/Tools/XCI/XCIGamecardCert.java +++ b/src/main/java/libKonogonka/Tools/XCI/XCIGamecardCert.java @@ -18,20 +18,26 @@ */ package libKonogonka.Tools.XCI; +import libKonogonka.Converter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import java.util.Arrays; /** * Gamecard Cert * */ public class XCIGamecardCert { - private byte[] rsa2048PKCS1sig; - private byte[] magicCert; - private byte[] unknown1; - private byte kekIndex; - private byte[] unknown2; - private byte[] deviceID; - private byte[] unknown3; - private byte[] encryptedData; + private final static Logger log = LogManager.getLogger(XCIGamecardCert.class); + + private final byte[] rsa2048PKCS1sig; + private final byte[] magicCert; + private final byte[] unknown1; + private final byte kekIndex; + private final byte[] unknown2; + private final byte[] deviceID; + private final byte[] unknown3; + private final byte[] encryptedData; XCIGamecardCert(byte[] certBytes) throws Exception{ if (certBytes.length != 512) @@ -44,16 +50,6 @@ public class XCIGamecardCert { deviceID = Arrays.copyOfRange(certBytes, 272, 288); unknown3 = Arrays.copyOfRange(certBytes, 288, 298); encryptedData = Arrays.copyOfRange(certBytes, 298, 512); - /* - RainbowHexDump.hexDumpUTF8(rsa2048PKCS1sig); - RainbowHexDump.hexDumpUTF8(magicCert); - RainbowHexDump.hexDumpUTF8(unknown1); - System.out.println(kekIndex); - RainbowHexDump.hexDumpUTF8(unknown2); - RainbowHexDump.hexDumpUTF8(deviceID); - RainbowHexDump.hexDumpUTF8(unknown3); - RainbowHexDump.hexDumpUTF8(encryptedData); - */ } public byte[] getRsa2048PKCS1sig() { return rsa2048PKCS1sig; } public byte[] getMagicCert() { return magicCert; } @@ -64,4 +60,17 @@ public class XCIGamecardCert { public byte[] getDeviceID() { return deviceID; } public byte[] getUnknown3() { return unknown3; } public byte[] getEncryptedData() { return encryptedData; } + + public void printDebug(){ + log.debug("== XCIGamecardCert ==\n" + + "rsa2048PKCS1sig " + Converter.byteArrToHexString(rsa2048PKCS1sig) + "\n" + + "magicCert " + Converter.byteArrToHexString(magicCert) + "\n" + + "unknown1 " + Converter.byteArrToHexString(unknown1) + "\n" + + "kekIndex " + kekIndex + "\n" + + "unknown2 " + Converter.byteArrToHexString(unknown2) + "\n" + + "deviceID " + Converter.byteArrToHexString(deviceID) + "\n" + + "unknown3 " + Converter.byteArrToHexString(unknown3) + "\n" + + "encryptedData " + Converter.byteArrToHexString(encryptedData) + "\n" + ); + } } diff --git a/src/main/java/libKonogonka/Tools/XCI/XCIGamecardInfo.java b/src/main/java/libKonogonka/Tools/XCI/XCIGamecardInfo.java index 569971d..11c924e 100644 --- a/src/main/java/libKonogonka/Tools/XCI/XCIGamecardInfo.java +++ b/src/main/java/libKonogonka/Tools/XCI/XCIGamecardInfo.java @@ -30,6 +30,7 @@ import static libKonogonka.Converter.getLElong; * Gamecard Info * */ public class XCIGamecardInfo{ + //private final static Logger log = LogManager.getLogger(XCIGamecardInfo.class); private long fwVersion; private byte[] accessCtrlFlags; // 0x00A10011 for 25MHz access or 0x00A10010 for 50MHz access @@ -73,12 +74,12 @@ public class XCIGamecardInfo{ cupID = Arrays.copyOfRange(decrypted, 48, 56); emptyPadding2 = Arrays.copyOfRange(decrypted, 56, 112); /* - System.out.println(fwVersion); + log.debug(fwVersion); RainbowHexDump.hexDumpUTF8(accessCtrlFlags); - System.out.println(readWaitTime1); - System.out.println(readWaitTime2); - System.out.println(writeWaitTime1); - System.out.println(writeWaitTime2); + log.debug(readWaitTime1); + log.debug(readWaitTime2); + log.debug(writeWaitTime1); + log.debug(writeWaitTime2); RainbowHexDump.hexDumpUTF8(fwMode); RainbowHexDump.hexDumpUTF8(cupVersion); RainbowHexDump.hexDumpUTF8(emptyPadding1); diff --git a/src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java b/src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java index 45eddf3..7a88237 100644 --- a/src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java +++ b/src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java @@ -88,7 +88,7 @@ public class AesCtrBufferedInputStream extends BufferedInputStream { //1 System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock); //2 - System.out.println("\n"+bytesFromFirstBlock+"\n"+ middleBlocksCount+" = "+(middleBlocksCount*0x200)+" bytes\n"+ bytesFromEnd+"\n"); + //log.debug("\n"+bytesFromFirstBlock+"\n"+ middleBlocksCount+" = "+(middleBlocksCount*0x200)+" bytes\n"+ bytesFromEnd+"\n"); for (int i = 0; i < middleBlocksCount; i++) { fillDecryptedCache(); System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200); diff --git a/src/test/java/libKonogonka/RomFsDecrypted/NSOTest.java b/src/test/java/libKonogonka/RomFsDecrypted/NSOTest.java index bf6b276..e0c9a24 100644 --- a/src/test/java/libKonogonka/RomFsDecrypted/NSOTest.java +++ b/src/test/java/libKonogonka/RomFsDecrypted/NSOTest.java @@ -30,7 +30,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.io.*; -import java.nio.file.Files; public class NSOTest { private static final String keysFileLocation = "./FilesForTests/prod.keys"; @@ -116,6 +115,7 @@ public class NSOTest { NSO0Provider nso0Provider = new NSO0Provider(pfs0Provider.getStreamProducer(0)); nso0Provider.printDebug(); + nso0Provider.exportAsDecompressedNSO0("./tmp"); // NPDMProvider npdmProvider = new NPDMProvider(pfs0Provider.getProviderSubFilePipedInpStream(1));