Adjust added classes to application domain standards; add interface for KIP1 extraction
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
e47d977779
commit
ce16e19985
13 changed files with 509 additions and 123 deletions
12
src/main/java/libKonogonka/IProducer.java
Normal file
12
src/main/java/libKonogonka/IProducer.java
Normal 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();
|
||||||
|
}
|
|
@ -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" +
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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" +
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(){
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue