Adjust added classes to application domain standards; add interface for KIP1 extraction
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Dmitry Isaenko 2023-01-08 00:00:47 +03:00
parent e47d977779
commit ce16e19985
13 changed files with 509 additions and 123 deletions

View file

@ -0,0 +1,12 @@
package libKonogonka;
import java.io.BufferedInputStream;
import java.io.File;
public interface IProducer {
BufferedInputStream produce() throws Exception;
IProducer getSuccessor(long subOffset);
boolean isEncrypted();
File getFile();
}

View file

@ -133,19 +133,19 @@ public class NSO0Header {
"SegmentHeader for .text\n" + "SegmentHeader for .text\n" +
" |- File Offset - - - - - - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getSegmentOffset()) + "\n" + " |- File Offset - - - - - - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getSegmentOffset()) + "\n" +
" |- Memory Offset - - - - - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getMemoryOffset()) + "\n" + " |- Memory Offset - - - - - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getMemoryOffset()) + "\n" +
" |- Size As Decompressed - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getSizeAsDecompressed()) + "\n" + " |- Size As Decompressed - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getSize()) + "\n" +
"ModuleNameOffset (calculated by sizeof(header)) " + RainbowDump.formatDecHexString(moduleNameOffset) + "\n" + "ModuleNameOffset (calculated by sizeof(header)) " + RainbowDump.formatDecHexString(moduleNameOffset) + "\n" +
" +++\n"+ " +++\n"+
"SegmentHeader for .rodata\n" + "SegmentHeader for .rodata\n" +
" |- File Offset - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getSegmentOffset()) + "\n" + " |- File Offset - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getSegmentOffset()) + "\n" +
" |- Memory Offset - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getMemoryOffset()) + "\n" + " |- Memory Offset - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getMemoryOffset()) + "\n" +
" |- Size As Decompressed - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getSizeAsDecompressed()) + "\n" + " |- Size As Decompressed - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getSize()) + "\n" +
"ModuleNameSize " + RainbowDump.formatDecHexString(moduleNameSize) + "\n" + "ModuleNameSize " + RainbowDump.formatDecHexString(moduleNameSize) + "\n" +
" +++\n"+ " +++\n"+
"SegmentHeader for .data\n" + "SegmentHeader for .data\n" +
" |- File Offset - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getSegmentOffset()) + "\n" + " |- File Offset - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getSegmentOffset()) + "\n" +
" |- Memory Offset - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getMemoryOffset()) + "\n" + " |- Memory Offset - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getMemoryOffset()) + "\n" +
" |- Size As Decompressed - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getSizeAsDecompressed()) + "\n" + " |- Size As Decompressed - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getSize()) + "\n" +
" .bss Size " + RainbowDump.formatDecHexString(bssSize) + "\n" + // Block Started by Symbol " .bss Size " + RainbowDump.formatDecHexString(bssSize) + "\n" + // Block Started by Symbol
"Module ID (aka Build ID) " + Converter.byteArrToHexStringAsLE(moduleId) + "\n" + "Module ID (aka Build ID) " + Converter.byteArrToHexStringAsLE(moduleId) + "\n" +
" .text Size (compressed) " + RainbowDump.formatDecHexString(textCompressedSize) + "\n" + " .text Size (compressed) " + RainbowDump.formatDecHexString(textCompressedSize) + "\n" +

View file

@ -98,7 +98,7 @@ class NSO0Unpacker {
private byte[] decompressSection(SegmentHeader segmentHeader, int compressedSectionSize) throws Exception{ private byte[] decompressSection(SegmentHeader segmentHeader, int compressedSectionSize) throws Exception{
try (BufferedInputStream stream = producer.produce()) { try (BufferedInputStream stream = producer.produce()) {
int sectionDecompressedSize = segmentHeader.getSizeAsDecompressed(); int sectionDecompressedSize = segmentHeader.getSize();
byte[] compressed = new byte[compressedSectionSize]; byte[] compressed = new byte[compressedSectionSize];
if (segmentHeader.getSegmentOffset() != stream.skip(segmentHeader.getSegmentOffset())) if (segmentHeader.getSegmentOffset() != stream.skip(segmentHeader.getSegmentOffset()))
@ -121,7 +121,7 @@ class NSO0Unpacker {
private byte[] duplicateSection(SegmentHeader segmentHeader) throws Exception{ private byte[] duplicateSection(SegmentHeader segmentHeader) throws Exception{
try (BufferedInputStream stream = producer.produce()) { try (BufferedInputStream stream = producer.produce()) {
int size = segmentHeader.getSizeAsDecompressed(); int size = segmentHeader.getSize();
byte[] sectionContent = new byte[size]; byte[] sectionContent = new byte[size];
if (segmentHeader.getSegmentOffset() != stream.skip(segmentHeader.getSegmentOffset())) if (segmentHeader.getSegmentOffset() != stream.skip(segmentHeader.getSegmentOffset()))
@ -183,20 +183,20 @@ class NSO0Unpacker {
.putInt(nso0Header.getFlags() & 0b111000) .putInt(nso0Header.getFlags() & 0b111000)
.putInt(textFileOffsetNew) .putInt(textFileOffsetNew)
.putInt(nso0Header.getTextSegmentHeader().getMemoryOffset()) .putInt(nso0Header.getTextSegmentHeader().getMemoryOffset())
.putInt(nso0Header.getTextSegmentHeader().getSizeAsDecompressed()) .putInt(nso0Header.getTextSegmentHeader().getSize())
.putInt(0x100) .putInt(0x100)
.putInt(rodataFileOffsetNew) .putInt(rodataFileOffsetNew)
.putInt(nso0Header.getRodataSegmentHeader().getMemoryOffset()) .putInt(nso0Header.getRodataSegmentHeader().getMemoryOffset())
.putInt(nso0Header.getRodataSegmentHeader().getSizeAsDecompressed()) .putInt(nso0Header.getRodataSegmentHeader().getSize())
.putInt(0) .putInt(0)
.putInt(dataFileOffsetNew) .putInt(dataFileOffsetNew)
.putInt(nso0Header.getDataSegmentHeader().getMemoryOffset()) .putInt(nso0Header.getDataSegmentHeader().getMemoryOffset())
.putInt(nso0Header.getDataSegmentHeader().getSizeAsDecompressed()) .putInt(nso0Header.getDataSegmentHeader().getSize())
.putInt(nso0Header.getBssSize()) .putInt(nso0Header.getBssSize())
.put(nso0Header.getModuleId()) .put(nso0Header.getModuleId())
.putInt(nso0Header.getTextSegmentHeader().getSizeAsDecompressed()) .putInt(nso0Header.getTextSegmentHeader().getSize())
.putInt(nso0Header.getRodataSegmentHeader().getSizeAsDecompressed()) .putInt(nso0Header.getRodataSegmentHeader().getSize())
.putInt(nso0Header.getDataSegmentHeader().getSizeAsDecompressed()) .putInt(nso0Header.getDataSegmentHeader().getSize())
.put(nso0Header.getBottomReserved()) .put(nso0Header.getBottomReserved())
.putInt(nso0Header.get_api_infoRelative().getOffset()) .putInt(nso0Header.get_api_infoRelative().getOffset())
.putInt(nso0Header.get_api_infoRelative().getSize()) .putInt(nso0Header.get_api_infoRelative().getSize())

View file

@ -43,9 +43,10 @@ public class SegmentHeader {
return memoryOffset; return memoryOffset;
} }
/** /**
* @return Size of compressed if used in KIP1 * SegmentHeader used in both NSO0 and KIP1 structures
* @return Size as decompressed if used in NSO0; size of compressed if used in KIP1.
* */ * */
public int getSizeAsDecompressed() { public int getSize() {
return sizeAsDecompressed; return sizeAsDecompressed;
} }
} }

View file

@ -21,10 +21,7 @@ package libKonogonka.Tools.other.System2.ini1;
import libKonogonka.Tools.ExportAble; import libKonogonka.Tools.ExportAble;
import libKonogonka.Tools.other.System2.KernelMap; import libKonogonka.Tools.other.System2.KernelMap;
import libKonogonka.Tools.other.System2.System2Header; import libKonogonka.Tools.other.System2.System2Header;
import libKonogonka.ctraesclassic.AesCtrClassicBufferedInputStream; import libKonogonka.ctraesclassic.InFileStreamClassicProducer;
import libKonogonka.ctraesclassic.AesCtrDecryptClassic;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@ -33,34 +30,22 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class Ini1Provider extends ExportAble { public class Ini1Provider extends ExportAble {
private final System2Header system2Header;
private final String pathToFile;
private final KernelMap kernelMap;
private Ini1Header ini1Header; private Ini1Header ini1Header;
private List<Kip1> kip1List; private List<KIP1Provider> kip1List;
private final InFileStreamClassicProducer producer;
public Ini1Provider(System2Header system2Header, String pathToFile, KernelMap kernelMap) throws Exception{ public Ini1Provider(System2Header system2Header, String pathToFile, KernelMap kernelMap) throws Exception{
this.system2Header = system2Header;
this.pathToFile = pathToFile;
this.kernelMap = kernelMap;
makeStream();
makeHeader();
collectKips();
}
private void makeStream() throws Exception{
Path filePath = Paths.get(pathToFile); Path filePath = Paths.get(pathToFile);
long toSkip = 0x200 + kernelMap.getIni1Offset(); this.producer = new InFileStreamClassicProducer(filePath,
AesCtrDecryptClassic decryptor = new AesCtrDecryptClassic(system2Header.getKey(), system2Header.getSection0Ctr()); 0x200 + kernelMap.getIni1Offset(),
stream = new AesCtrClassicBufferedInputStream(decryptor,
0x200, 0x200,
Files.size(filePath), // size of system2 Files.size(filePath), // size of system2
Files.newInputStream(filePath), system2Header.getKey(),
Files.size(filePath)); system2Header.getSection0Ctr());
stream = producer.produce();
if (toSkip != stream.skip(toSkip)) makeHeader();
throw new Exception("Unable to skip offset: "+toSkip); collectKips();
} }
private void makeHeader() throws Exception{ private void makeHeader() throws Exception{
@ -80,29 +65,22 @@ public class Ini1Provider extends ExportAble {
byte[] kip1bytes = new byte[0x100]; byte[] kip1bytes = new byte[0x100];
if (0x100 != stream.read(kip1bytes)) if (0x100 != stream.read(kip1bytes))
throw new Exception("Unable to read KIP1 data "); throw new Exception("Unable to read KIP1 data ");
Kip1 kip1 = new Kip1(kip1bytes, kip1StartOffset); KIP1Provider kip1 = new KIP1Provider(kip1bytes, kip1StartOffset, producer.getSuccessor(0x10, true));
kip1List.add(kip1); kip1List.add(kip1);
skipTillNextKip1 = kip1.getTextSegmentHeader().getSizeAsDecompressed() + KIP1Header kip1Header = kip1.getHeader();
kip1.getRoDataSegmentHeader().getSizeAsDecompressed() + skipTillNextKip1 = kip1Header.getTextSegmentHeader().getSize() +
kip1.getRwDataSegmentHeader().getSizeAsDecompressed() + kip1Header.getRoDataSegmentHeader().getSize() +
kip1.getBssSegmentHeader().getSizeAsDecompressed(); kip1Header.getRwDataSegmentHeader().getSize() +
kip1Header.getBssSegmentHeader().getSize();
kip1StartOffset = kip1.getEndOffset(); kip1StartOffset = kip1.getEndOffset();
} }
} }
public Ini1Header getIni1Header() { return ini1Header; } public Ini1Header getIni1Header() { return ini1Header; }
public List<Kip1> getKip1List() { return kip1List; } public List<KIP1Provider> getKip1List() { return kip1List; }
public boolean exportIni1(String saveTo) throws Exception{ public boolean export(String saveTo) throws Exception{
makeStream(); stream = producer.produce();
return export(saveTo, "INI1.bin", 0, ini1Header.getSize()); return export(saveTo, "INI1.bin", 0, ini1Header.getSize());
} }
public boolean exportKip1(String saveTo, Kip1 kip1) throws Exception{
makeStream();
return export(saveTo,
kip1.getName()+".kip1",
0x10 + kip1.getStartOffset(),
kip1.getEndOffset()-kip1.getStartOffset());
}
} }

View file

@ -25,49 +25,30 @@ import libKonogonka.Tools.NSO.SegmentHeader;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays; import java.util.Arrays;
public class Kip1 { public class KIP1Header {
private final static Logger log = LogManager.getLogger(Kip1.class); private final static Logger log = LogManager.getLogger(KIP1Header.class);
private String magic; private final String magic;
private String name; private final String name;
private byte[] programId; private final byte[] programId;
private int version; private final int version;
private byte mainThreadPriority; private final byte mainThreadPriority;
private byte mainThreadCoreNumber; private final byte mainThreadCoreNumber;
private byte reserved1; private final byte reserved1;
private byte flags; private final byte flags;
private SegmentHeader textSegmentHeader; private final SegmentHeader textSegmentHeader;
private int threadAffinityMask; private final int threadAffinityMask;
private SegmentHeader roDataSegmentHeader; private final SegmentHeader roDataSegmentHeader;
private int mainThreadStackSize ; private final int mainThreadStackSize;
private SegmentHeader rwDataSegmentHeader; private final SegmentHeader rwDataSegmentHeader;
private byte[] reserved2; private final byte[] reserved2;
private SegmentHeader bssSegmentHeader; private final SegmentHeader bssSegmentHeader;
private byte[] reserved3; private final byte[] reserved3;
private KernelAccessControlProvider kernelCapabilityData; private final KernelAccessControlProvider kernelCapabilityData;
private long startOffset;
private long endOffset;
public Kip1(String fileLocation) throws Exception{
try (BufferedInputStream stream = new BufferedInputStream(Files.newInputStream(Paths.get(fileLocation)));) {
byte[] kip1HeaderBytes = new byte[0x100];
if (0x100 != stream.read(kip1HeaderBytes))
throw new Exception("Unable to read KIP1 file header");
makeHeader(kip1HeaderBytes, 0);
}
}
public Kip1(byte[] kip1HeaderBytes, long kip1StartOffset) throws Exception{ public KIP1Header(byte[] kip1HeaderBytes) throws Exception{
makeHeader(kip1HeaderBytes, kip1StartOffset);
}
private void makeHeader(byte[] kip1HeaderBytes, long kip1StartOffset) throws Exception{
this.magic = new String(kip1HeaderBytes, 0, 0x4); this.magic = new String(kip1HeaderBytes, 0, 0x4);
this.name = new String(kip1HeaderBytes, 0x4, 0xC).trim(); this.name = new String(kip1HeaderBytes, 0x4, 0xC).trim();
this.programId = Arrays.copyOfRange(kip1HeaderBytes, 0x10, 0x18); this.programId = Arrays.copyOfRange(kip1HeaderBytes, 0x10, 0x18);
@ -85,10 +66,6 @@ public class Kip1 {
this.bssSegmentHeader = new SegmentHeader(kip1HeaderBytes, 0x50); this.bssSegmentHeader = new SegmentHeader(kip1HeaderBytes, 0x50);
this.reserved3 = Arrays.copyOfRange(kip1HeaderBytes, 0x5c, 0x80); this.reserved3 = Arrays.copyOfRange(kip1HeaderBytes, 0x5c, 0x80);
this.kernelCapabilityData = new KernelAccessControlProvider(Arrays.copyOfRange(kip1HeaderBytes, 0x80, 0x100)); this.kernelCapabilityData = new KernelAccessControlProvider(Arrays.copyOfRange(kip1HeaderBytes, 0x80, 0x100));
this.startOffset = kip1StartOffset;
this.endOffset = 0x100 + kip1StartOffset + textSegmentHeader.getSizeAsDecompressed() + roDataSegmentHeader.getSizeAsDecompressed() +
rwDataSegmentHeader.getSizeAsDecompressed() + bssSegmentHeader.getSizeAsDecompressed();
} }
public String getMagic() { return magic; } public String getMagic() { return magic; }
@ -99,6 +76,12 @@ public class Kip1 {
public byte getMainThreadCoreNumber() { return mainThreadCoreNumber; } public byte getMainThreadCoreNumber() { return mainThreadCoreNumber; }
public byte getReserved1() { return reserved1; } public byte getReserved1() { return reserved1; }
public byte getFlags() { return flags; } public byte getFlags() { return flags; }
public boolean isTextCompressFlag(){ return (flags & 1) == 1; }
public boolean isRoDataCompressFlag(){ return (flags >> 1 & 1) == 1; }
public boolean isRwDataCompressFlag(){ return (flags >> 2 & 1) == 1; }
public boolean is64BitInstruction(){ return (flags >> 3 & 1) == 1; }
public boolean isAddressSpace64Bit(){ return (flags >> 4 & 1) == 1; }
public boolean isUseSecureMemory(){ return (flags >> 5 & 1) == 1; }
public SegmentHeader getTextSegmentHeader() { return textSegmentHeader; } public SegmentHeader getTextSegmentHeader() { return textSegmentHeader; }
public int getThreadAffinityMask() { return threadAffinityMask; } public int getThreadAffinityMask() { return threadAffinityMask; }
public SegmentHeader getRoDataSegmentHeader() { return roDataSegmentHeader; } public SegmentHeader getRoDataSegmentHeader() { return roDataSegmentHeader; }
@ -109,9 +92,6 @@ public class Kip1 {
public byte[] getReserved3() { return reserved3; } public byte[] getReserved3() { return reserved3; }
public KernelAccessControlProvider getKernelCapabilityData() { return kernelCapabilityData; } public KernelAccessControlProvider getKernelCapabilityData() { return kernelCapabilityData; }
public long getStartOffset() { return startOffset; }
public long getEndOffset() { return endOffset; }
public void printDebug(){ public void printDebug(){
StringBuilder mapIoOrNormalRange = new StringBuilder(); StringBuilder mapIoOrNormalRange = new StringBuilder();
StringBuilder interruptPairs = new StringBuilder(); StringBuilder interruptPairs = new StringBuilder();
@ -141,7 +121,7 @@ public class Kip1 {
syscallMasks.append("\n"); syscallMasks.append("\n");
}); });
log.debug(String.format("..:: KIP1 (0x%x-0x%x) ::..%n", startOffset, endOffset) + log.debug("..:: KIP1 ::..\n" +
"Magic : " + magic + "\n" + "Magic : " + magic + "\n" +
"Name : " + name + "\n" + "Name : " + name + "\n" +
"ProgramId : " + Converter.byteArrToHexStringAsLE(programId) + "\n" + "ProgramId : " + Converter.byteArrToHexStringAsLE(programId) + "\n" +
@ -159,22 +139,22 @@ public class Kip1 {
".text segment header\n" + ".text segment header\n" +
" Segment offset : " + RainbowDump.formatDecHexString(textSegmentHeader.getSegmentOffset()) + "\n" + " Segment offset : " + RainbowDump.formatDecHexString(textSegmentHeader.getSegmentOffset()) + "\n" +
" Memory offset : " + RainbowDump.formatDecHexString(textSegmentHeader.getMemoryOffset()) + "\n" + " Memory offset : " + RainbowDump.formatDecHexString(textSegmentHeader.getMemoryOffset()) + "\n" +
" Size : " + RainbowDump.formatDecHexString(textSegmentHeader.getSizeAsDecompressed()) + "\n" + " Size : " + RainbowDump.formatDecHexString(textSegmentHeader.getSize()) + "\n" +
"Thread affinity mask : " + RainbowDump.formatDecHexString(threadAffinityMask) + "\n" + "Thread affinity mask : " + RainbowDump.formatDecHexString(threadAffinityMask) + "\n" +
".ro segment header\n" + ".ro segment header\n" +
" Segment offset : " + RainbowDump.formatDecHexString(roDataSegmentHeader.getSegmentOffset()) + "\n" + " Segment offset : " + RainbowDump.formatDecHexString(roDataSegmentHeader.getSegmentOffset()) + "\n" +
" Memory offset : " + RainbowDump.formatDecHexString(roDataSegmentHeader.getMemoryOffset()) + "\n" + " Memory offset : " + RainbowDump.formatDecHexString(roDataSegmentHeader.getMemoryOffset()) + "\n" +
" Size : " + RainbowDump.formatDecHexString(roDataSegmentHeader.getSizeAsDecompressed()) + "\n" + " Size : " + RainbowDump.formatDecHexString(roDataSegmentHeader.getSize()) + "\n" +
"Main thread stack size : " + RainbowDump.formatDecHexString(mainThreadStackSize) + "\n" + "Main thread stack size : " + RainbowDump.formatDecHexString(mainThreadStackSize) + "\n" +
".rw segment header\n" + ".rw segment header\n" +
" Segment offset : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getSegmentOffset()) + "\n" + " Segment offset : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getSegmentOffset()) + "\n" +
" Memory offset : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getMemoryOffset()) + "\n" + " Memory offset : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getMemoryOffset()) + "\n" +
" Size : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getSizeAsDecompressed()) + "\n" + " Size : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getSize()) + "\n" +
"Reserved 2 : " + Converter.byteArrToHexStringAsLE(reserved2) + "\n" + "Reserved 2 : " + Converter.byteArrToHexStringAsLE(reserved2) + "\n" +
".bss segment header\n" + ".bss segment header\n" +
" Segment offset : " + RainbowDump.formatDecHexString(bssSegmentHeader.getSegmentOffset()) + "\n" + " Segment offset : " + RainbowDump.formatDecHexString(bssSegmentHeader.getSegmentOffset()) + "\n" +
" Memory offset : " + RainbowDump.formatDecHexString(bssSegmentHeader.getMemoryOffset()) + "\n" + " Memory offset : " + RainbowDump.formatDecHexString(bssSegmentHeader.getMemoryOffset()) + "\n" +
" Size : " + RainbowDump.formatDecHexString(bssSegmentHeader.getSizeAsDecompressed()) + "\n" + " Size : " + RainbowDump.formatDecHexString(bssSegmentHeader.getSize()) + "\n" +
"Reserved 3 : " + Converter.byteArrToHexStringAsLE(reserved3) + "\n" + "Reserved 3 : " + Converter.byteArrToHexStringAsLE(reserved3) + "\n" +
"Kernel capability data\n" + "Kernel capability data\n" +
" Kernel flags available? : " + kernelCapabilityData.isKernelFlagsAvailable() + "\n" + " Kernel flags available? : " + kernelCapabilityData.isKernelFlagsAvailable() + "\n" +

View file

@ -0,0 +1,82 @@
/*
Copyright 2019-2023 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.Tools.other.System2.ini1;
import libKonogonka.Tools.ExportAble;
import libKonogonka.ctraesclassic.InFileStreamClassicProducer;
import java.nio.file.Paths;
public class KIP1Provider extends ExportAble {
private KIP1Header header;
private final InFileStreamClassicProducer producer;
private long startOffset;
private long endOffset;
public KIP1Provider(String fileLocation) throws Exception{
this.producer = new InFileStreamClassicProducer(Paths.get(fileLocation));
this.stream = producer.produce();
byte[] kip1HeaderBytes = new byte[0x100];
if (0x100 != stream.read(kip1HeaderBytes))
throw new Exception("Unable to read KIP1 file header");
makeHeader(kip1HeaderBytes);
calculateOffsets(0);
}
public KIP1Provider(byte[] kip1HeaderBytes, long kip1StartOffset, InFileStreamClassicProducer producer) throws Exception{
makeHeader(kip1HeaderBytes);
calculateOffsets(kip1StartOffset);
this.producer = producer;
this.stream = producer.produce();
}
private void makeHeader(byte[] kip1HeaderBytes) throws Exception{
this.header = new KIP1Header(kip1HeaderBytes);
}
private void calculateOffsets(long kip1StartOffset){
this.startOffset = kip1StartOffset;
this.endOffset = 0x100 + kip1StartOffset +
header.getTextSegmentHeader().getSize() + header.getRoDataSegmentHeader().getSize() +
header.getRwDataSegmentHeader().getSize() + header.getBssSegmentHeader().getSize();
}
public KIP1Header getHeader() { return header; }
public long getStartOffset() { return startOffset; }
public long getEndOffset() { return endOffset; }
public boolean export(String saveTo) throws Exception{
stream = producer.produce();
return export(saveTo, header.getName()+".kip1", startOffset, endOffset - startOffset);
}
public boolean exportAsDecompressed(String saveToLocation) throws Exception{
return Kip1Unpacker.unpack(header, producer, saveToLocation);
}
public KIP1Raw getAsDecompressed() throws Exception{
return Kip1Unpacker.getNSO0Raw(header, producer);
}
public void printDebug(){
header.printDebug();
}
}

View file

@ -0,0 +1,47 @@
/*
Copyright 2019-2023 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.Tools.other.System2.ini1;
public class KIP1Raw {
private KIP1Header headerObject;
private final byte[] header;
private final byte[] _textDecompressedSection;
private final byte[] _rodataDecompressedSection;
private final byte[] _dataDecompressedSection;
KIP1Raw(byte[] header,
byte[] _textDecompressedSection,
byte[] _rodataDecompressedSection,
byte[] _dataDecompressedSection){
this.header = header;
this._textDecompressedSection = _textDecompressedSection;
this. _rodataDecompressedSection = _rodataDecompressedSection;
this._dataDecompressedSection = _dataDecompressedSection;
try {
this.headerObject = new KIP1Header(header);
}
catch (Exception e){ e.printStackTrace(); }
}
public KIP1Header getHeader() { return headerObject; }
public byte[] getHeaderRaw() {return header;}
public byte[] getTextRaw() {return _textDecompressedSection;}
public byte[] getRodataRaw() {return _rodataDecompressedSection;}
public byte[] getDataRaw() {return _dataDecompressedSection;}
}

View file

@ -0,0 +1,151 @@
/*
Copyright 2019-2023 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.Tools.other.System2.ini1;
import libKonogonka.Tools.NSO.SegmentHeader;
import libKonogonka.ctraesclassic.InFileStreamClassicProducer;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
public class Kip1Unpacker {
private static final String DECOMPRESSED_FILE_POSTFIX = "_decompressed";
private final KIP1Header kip1Header;
private final InFileStreamClassicProducer producer;
private byte[] header;
private byte[] _textDecompressedSection;
private byte[] _roDataDecompressedSection;
private byte[] _rwDataDecompressedSection;
private int textFileOffsetNew;
private int roDataFileOffsetNew;
private int rwDataFileOffsetNew;
private Kip1Unpacker(KIP1Header kip1Header, InFileStreamClassicProducer producer) throws Exception{
this.kip1Header = kip1Header;
this.producer = producer;
decompressSections();
makeHeader();
}
static boolean unpack(KIP1Header kip1Header, InFileStreamClassicProducer producer, String saveToLocation) throws Exception{
if (! kip1Header.isTextCompressFlag() && ! kip1Header.isRoDataCompressFlag() && ! kip1Header.isRwDataCompressFlag())
throw new Exception("This file is not compressed. Use 'export(location)' method instead.");
Kip1Unpacker instance = new Kip1Unpacker(kip1Header, producer);
instance.writeFile(saveToLocation);
return true;
}
static KIP1Raw getNSO0Raw(KIP1Header kip1Header, InFileStreamClassicProducer producer) throws Exception{
Kip1Unpacker instance = new Kip1Unpacker(kip1Header, producer);
return new KIP1Raw(instance.header,
instance._textDecompressedSection,
instance._roDataDecompressedSection,
instance._rwDataDecompressedSection);
}
private void decompressSections() throws Exception{
decompressTextSection();
decompressRodataSection();
decompressDataSection();
}
private void decompressTextSection() throws Exception{
if (kip1Header.isTextCompressFlag())
_textDecompressedSection = decompressSection(kip1Header.getTextSegmentHeader(), kip1Header.getTextSegmentHeader().getSize());
else
_textDecompressedSection = duplicateSection(kip1Header.getTextSegmentHeader());
}
private void decompressRodataSection() throws Exception{
if (kip1Header.isRoDataCompressFlag())
_roDataDecompressedSection = decompressSection(kip1Header.getRoDataSegmentHeader(), kip1Header.getRoDataSegmentHeader().getSize());
else
_roDataDecompressedSection = duplicateSection(kip1Header.getRoDataSegmentHeader());
}
private void decompressDataSection() throws Exception{
if (kip1Header.isRwDataCompressFlag())
_rwDataDecompressedSection = decompressSection(kip1Header.getRwDataSegmentHeader(), kip1Header.getRwDataSegmentHeader().getSize());
else
_rwDataDecompressedSection = duplicateSection(kip1Header.getRwDataSegmentHeader());
}
private byte[] decompressSection(SegmentHeader segmentHeader, int compressedSectionSize) throws Exception{
// TODO
return new byte[1];
}
private byte[] duplicateSection(SegmentHeader segmentHeader) throws Exception{
try (BufferedInputStream stream = producer.produce()) {
int size = segmentHeader.getSize();
byte[] sectionContent = new byte[size];
if (segmentHeader.getSegmentOffset() != stream.skip(segmentHeader.getSegmentOffset()))
throw new Exception("Failed to skip " + segmentHeader.getSegmentOffset() + " bytes till section");
if (size != stream.read(sectionContent))
throw new Exception("Failed to read entire section");
return sectionContent;
}
}
private void makeHeader() throws Exception{
try (BufferedInputStream stream = producer.produce()) {
byte[] headerBytes = new byte[0x100];
if (0x100 != stream.read(headerBytes))
throw new Exception("Unable to read initial 0x100 bytes needed for export.");
//TODO
//textFileOffsetNew = kip1Header.getTextSegmentHeader().getMemoryOffset()+0x100;
//roDataFileOffsetNew = kip1Header.getRoDataSegmentHeader().getMemoryOffset()+0x100;
//rwDataFileOffsetNew = kip1Header.getRwDataSegmentHeader().getMemoryOffset()+0x100;
ByteBuffer resultingHeader = ByteBuffer.allocate(0x100).order(ByteOrder.LITTLE_ENDIAN);
resultingHeader.put("KIP1".getBytes(StandardCharsets.US_ASCII));
//.putInt(kip1Header.getVersion())
//.put(kip1Header.getUpperReserved())
header = resultingHeader.array();
}
}
private void writeFile(String saveToLocation) throws Exception{
File location = new File(saveToLocation);
location.mkdirs();
try (RandomAccessFile raf = new RandomAccessFile(
saveToLocation+File.separator+kip1Header.getName()+DECOMPRESSED_FILE_POSTFIX+".kip1", "rw")){
raf.write(header);
raf.seek(textFileOffsetNew);
raf.write(_textDecompressedSection);
raf.seek(roDataFileOffsetNew);
raf.write(_roDataDecompressedSection);
raf.seek(roDataFileOffsetNew);
raf.write(_rwDataDecompressedSection);
}
}
}

View file

@ -18,11 +18,13 @@
*/ */
package libKonogonka.ctraes; package libKonogonka.ctraes;
import libKonogonka.IProducer;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
import java.nio.file.Files; import java.nio.file.Files;
public class InFileStreamProducer { public class InFileStreamProducer implements IProducer {
private boolean encrypted; private boolean encrypted;
private final File file; private final File file;
@ -57,7 +59,7 @@ public class InFileStreamProducer {
this.mediaStartOffset = mediaStartOffset; this.mediaStartOffset = mediaStartOffset;
this.mediaEndOffset = mediaEndOffset; this.mediaEndOffset = mediaEndOffset;
} }
@Override
public BufferedInputStream produce() throws Exception{ public BufferedInputStream produce() throws Exception{
if (encrypted) if (encrypted)
return produceAesCtr(); return produceAesCtr();
@ -80,12 +82,12 @@ public class InFileStreamProducer {
skipBytesTillBeginning(stream, subOffset); skipBytesTillBeginning(stream, subOffset);
return stream; return stream;
} }
@Override
public InFileStreamProducer getSuccessor(long subOffset){ public InFileStreamProducer getSuccessor(long subOffset){
this.subOffset = subOffset; this.subOffset = subOffset;
return new InFileStreamProducer(file, initialOffset, subOffset, decryptor, mediaStartOffset, mediaEndOffset); return new InFileStreamProducer(file, initialOffset, subOffset, decryptor, mediaStartOffset, mediaEndOffset);
} }
@Override
public boolean isEncrypted() { public boolean isEncrypted() {
return encrypted; return encrypted;
} }
@ -98,7 +100,7 @@ public class InFileStreamProducer {
mustSkip = size - skipped; mustSkip = size - skipped;
} }
} }
@Override
public File getFile(){ return file; } public File getFile(){ return file; }
@Override @Override
public String toString(){ public String toString(){

View file

@ -59,9 +59,7 @@ public class AesCtrDecryptClassic {
* @param blocks - how many blocks from encrypted section start should be skipped. Block size = 0x200 * @param blocks - how many blocks from encrypted section start should be skipped. Block size = 0x200
* */ * */
public void resetAndSkip(long blocks) throws Exception{ public void resetAndSkip(long blocks) throws Exception{
cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC"); reset(calculateCtr(blocks * 0x200));
IvParameterSpec iv = new IvParameterSpec(calculateCtr(blocks * 0x200));
cipher.init(Cipher.DECRYPT_MODE, key, iv);
} }
private byte[] calculateCtr(long offset){ private byte[] calculateCtr(long offset){
BigInteger ctr = new BigInteger(ivArray); BigInteger ctr = new BigInteger(ivArray);
@ -69,6 +67,19 @@ public class AesCtrDecryptClassic {
return ctr.add(updateTo).toByteArray(); return ctr.add(updateTo).toByteArray();
} }
/**
* Initializes cipher again using initial IV
* */
public void reset() throws Exception{
reset(ivArray.clone());
}
private void reset(byte[] updatedIvArray) throws Exception{
cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
IvParameterSpec iv = new IvParameterSpec(updatedIvArray);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
}
private byte[] hexStrToByteArray(String s) { private byte[] hexStrToByteArray(String s) {
int len = s.length(); int len = s.length();
byte[] data = new byte[len / 2]; byte[] data = new byte[len / 2];

View file

@ -0,0 +1,121 @@
/*
Copyright 2019-2023 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.ctraesclassic;
import libKonogonka.IProducer;
import java.io.BufferedInputStream;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
public class InFileStreamClassicProducer implements IProducer {
private boolean encrypted;
private final Path filePath;
private long offset;
private long encryptedStartOffset;
private long encryptedEndOffset;
private AesCtrDecryptClassic decryptor;
public InFileStreamClassicProducer(Path filePath,
long offset,
long encryptedStartOffset,
long encryptedEndOffset, //Files.size(filePath)
String key,
byte[] iv) throws Exception{
this.encrypted = true;
this.filePath = filePath;
this.offset = offset;
this.encryptedStartOffset = encryptedStartOffset;
this.encryptedEndOffset = encryptedEndOffset;
this.decryptor = new AesCtrDecryptClassic(key, iv);
}
public InFileStreamClassicProducer(Path filePath,
long offset,
long encryptedStartOffset,
long encryptedEndOffset, //Files.size(filePath)
AesCtrDecryptClassic decryptor){
this.encrypted = true;
this.filePath = filePath;
this.offset = offset;
this.encryptedStartOffset = encryptedStartOffset;
this.encryptedEndOffset = encryptedEndOffset;
this.decryptor = decryptor;
}
public InFileStreamClassicProducer(Path filePath){
this.filePath = filePath;
}
public InFileStreamClassicProducer(Path filePath, long offset){
this.filePath = filePath;
this.offset = offset;
}
@Override
public BufferedInputStream produce() throws Exception{
if (encrypted)
return produceAesCtr();
else
return produceNotEncrypted();
}
private BufferedInputStream produceAesCtr() throws Exception{
decryptor.reset();
AesCtrClassicBufferedInputStream stream = new AesCtrClassicBufferedInputStream(decryptor,
encryptedStartOffset,
encryptedEndOffset,
Files.newInputStream(filePath),
Files.size(filePath));
if (offset != stream.skip(offset))
throw new Exception("Unable to skip offset: "+offset);
return stream;
}
private BufferedInputStream produceNotEncrypted() throws Exception{
BufferedInputStream stream = new BufferedInputStream(Files.newInputStream(filePath));
if (offset != stream.skip(offset))
throw new Exception("Unable to skip offset: "+offset);
return stream;
}
@Override
public InFileStreamClassicProducer getSuccessor(long offset){
if (encrypted)
return new InFileStreamClassicProducer(filePath, offset, encryptedStartOffset, encryptedEndOffset, decryptor);
return new InFileStreamClassicProducer(filePath, offset);
}
public InFileStreamClassicProducer getSuccessor(long offset, boolean incrementExisting){
if (incrementExisting)
return getSuccessor(this.offset + offset);
return getSuccessor(offset);
}
@Override
public boolean isEncrypted() {
return encrypted;
}
@Override
public File getFile(){ return filePath.toFile(); }
@Override
public String toString(){
return filePath.toString();
}
}

View file

@ -21,7 +21,7 @@ package libKonogonka.RomFsDecrypted;
import libKonogonka.KeyChainHolder; import libKonogonka.KeyChainHolder;
import libKonogonka.Tools.other.System2.System2Provider; import libKonogonka.Tools.other.System2.System2Provider;
import libKonogonka.Tools.other.System2.ini1.Ini1Provider; import libKonogonka.Tools.other.System2.ini1.Ini1Provider;
import libKonogonka.Tools.other.System2.ini1.Kip1; import libKonogonka.Tools.other.System2.ini1.KIP1Provider;
import libKonogonka.ctraesclassic.AesCtrDecryptClassic; import libKonogonka.ctraesclassic.AesCtrDecryptClassic;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
@ -101,23 +101,24 @@ public class Package2UnpackedTest {
provider.getKernelMap().printDebug(); provider.getKernelMap().printDebug();
Ini1Provider ini1Provider = provider.getIni1Provider(); Ini1Provider ini1Provider = provider.getIni1Provider();
ini1Provider.getIni1Header().printDebug(); ini1Provider.getIni1Header().printDebug();
for (Kip1 kip1 : ini1Provider.getKip1List()) for (KIP1Provider kip1Provider : ini1Provider.getKip1List())
kip1.printDebug(); kip1Provider.printDebug();
boolean exported = provider.exportKernel("/home/loper/Projects/libKonogonka/FilesForTests/own/"); boolean exported = provider.exportKernel("/home/loper/Projects/libKonogonka/FilesForTests/own/");
System.out.println("Exported = "+exported); System.out.println("Exported = "+exported);
exported = ini1Provider.exportIni1("/home/loper/Projects/libKonogonka/FilesForTests/own/"); exported = ini1Provider.export("/home/loper/Projects/libKonogonka/FilesForTests/own/");
System.out.println("Exported INI1 = "+exported); System.out.println("Exported INI1 = "+exported);
for (Kip1 kip1 : ini1Provider.getKip1List()) { for (KIP1Provider kip1Provider : ini1Provider.getKip1List()) {
exported = ini1Provider.exportKip1("/home/loper/Projects/libKonogonka/FilesForTests/own/KIP1s", kip1); exported = kip1Provider.export("/home/loper/Projects/libKonogonka/FilesForTests/own/KIP1s");
System.out.println("Exported KIP1s "+ kip1.getName() +" = " + exported + String.format(" Size 0x%x", Files.size(Paths.get("/home/loper/Projects/libKonogonka/FilesForTests/own/KIP1s/"+kip1.getName()+".kip1")))); System.out.println("Exported KIP1s "+ kip1Provider.getHeader().getName() +" = " + exported +
String.format(" Size 0x%x", Files.size(Paths.get("/home/loper/Projects/libKonogonka/FilesForTests/own/KIP1s/"+ kip1Provider.getHeader().getName()+".kip1"))));
} }
} }
@DisplayName("KIP1 read reference") @DisplayName("KIP1 read reference")
@Test @Test
void checkReference() throws Exception{ void checkReference() throws Exception{
Kip1 kip1 = new Kip1("/home/loper/Projects/libKonogonka/FilesForTests/"); KIP1Provider kip1Provider = new KIP1Provider("/home/loper/Projects/libKonogonka/FilesForTests/FS.kip1-fat.dec");
kip1.printDebug(); kip1Provider.printDebug();
} }
} }