diff --git a/README.md b/README.md index 1d188ff..09f5132 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,10 @@ JRE/JDK 8u60 or higher. * [X] TIK * [X] XML * [X] NRO -* [ ] LogPrinter to singleton implementation. * [ ] NPDM support +* [ ] CERT support * [ ] CNMT support * [ ] NSO support -* [ ] RomFS deep-dive -* [ ] 'Save to folder' option \ No newline at end of file +* [ ] RomFS deep-dive +* [ ] LogPrinter to singleton implementation. +* [X] 'Save to folder' option \ No newline at end of file diff --git a/src/main/java/konogonka/Controllers/NPDM/FSAccessControlController.java b/src/main/java/konogonka/Controllers/NPDM/FSAccessControlController.java index 0cfe26d..8d585c7 100644 --- a/src/main/java/konogonka/Controllers/NPDM/FSAccessControlController.java +++ b/src/main/java/konogonka/Controllers/NPDM/FSAccessControlController.java @@ -10,6 +10,7 @@ import java.net.URL; import java.util.ResourceBundle; import static konogonka.LoperConverter.byteArrToHexString; +import static konogonka.LoperConverter.longToOctString; public class FSAccessControlController implements Initializable { @FXML @@ -168,20 +169,12 @@ public class FSAccessControlController implements Initializable { public void populateFields(FSAccessControlProvider provider){ ACID_FSAcccessControlVersionLbl.setText(String.format("0x%02x", provider.getVersion())); ACID_FSAcccessControlPaddingLbl.setText(byteArrToHexString(provider.getPadding())); - ACID_FSAcccessControlBitbaskLbl.setText(byteArrToHexString(provider.getPermissionsBitmask())); + StringBuilder sb = new StringBuilder(longToOctString(provider.getPermissionsBitmask())); + sb.reverse(); + String mask = sb.toString(); + ACID_FSAcccessControlBitbaskLbl.setText(mask); ACID_FSAcccessControlReservedTf.setText(byteArrToHexString(provider.getReserved())); - byte[] permissionsBitmask = provider.getPermissionsBitmask(); - String mask = - String.format("%8s", Integer.toBinaryString(permissionsBitmask[7] & 0xFF)).replace(' ', '0')+ - String.format("%8s", Integer.toBinaryString(permissionsBitmask[6] & 0xFF)).replace(' ', '0')+ - String.format("%8s", Integer.toBinaryString(permissionsBitmask[5] & 0xFF)).replace(' ', '0')+ - String.format("%8s", Integer.toBinaryString(permissionsBitmask[4] & 0xFF)).replace(' ', '0')+ - String.format("%8s", Integer.toBinaryString(permissionsBitmask[3] & 0xFF)).replace(' ', '0')+ - String.format("%8s", Integer.toBinaryString(permissionsBitmask[2] & 0xFF)).replace(' ', '0')+ - String.format("%8s", Integer.toBinaryString(permissionsBitmask[1] & 0xFF)).replace(' ', '0')+ - String.format("%8s", Integer.toBinaryString(permissionsBitmask[0] & 0xFF)).replace(' ', '0'); - for (int i = 0; i < 64; i++) masksArr[i].setText(mask.substring(i, i+1)); } diff --git a/src/main/java/konogonka/Controllers/NPDM/KernelAccessControlController.java b/src/main/java/konogonka/Controllers/NPDM/KernelAccessControlController.java index 58df0a5..ea4b6f1 100644 --- a/src/main/java/konogonka/Controllers/NPDM/KernelAccessControlController.java +++ b/src/main/java/konogonka/Controllers/NPDM/KernelAccessControlController.java @@ -29,10 +29,7 @@ public class KernelAccessControlController { private Label mapNormalPageRwLbl; @FXML - private Label interruptPairAvalLbl, - irq0Lbl, - irq1Lbl; - + private VBox interruptPairsPane; @FXML private Label appTypeLbl, kerRelVerLbl, @@ -54,9 +51,8 @@ public class KernelAccessControlController { mapIoPane.getChildren().clear(); mapIoPane.getChildren().add(new Separator()); mapNormalPageRwLbl.setText("-"); - interruptPairAvalLbl.setText("?"); - irq0Lbl.setText("-"); - irq1Lbl.setText("-"); + interruptPairsPane.getChildren().clear(); + interruptPairsPane.getChildren().add(new Separator()); appTypeLbl.setText("-"); kerRelVerLbl.setText("-"); handleTableSizeLbl.setText("-"); @@ -115,9 +111,11 @@ public class KernelAccessControlController { roFlagLbl.setPadding(new Insets(5.0, 5.0, 5.0, 5.0)); roFlagVal.setPadding(new Insets(5.0, 5.0, 5.0, 5.0)); - mapIoPane.getChildren().add(new HBox(altStPgNnumOfPgLbl, altStPgNnumOfPgVal)); - mapIoPane.getChildren().add(new HBox(roFlagLbl, roFlagVal)); - mapIoPane.getChildren().add(new Separator()); + mapIoPane.getChildren().addAll( + new HBox(altStPgNnumOfPgLbl, altStPgNnumOfPgVal), + new HBox(roFlagLbl, roFlagVal), + new Separator() + ); } byte[] mapNormalPageRwBarr = kacProvider.getMapNormalPage(); if (mapNormalPageRwBarr != null){ @@ -128,22 +126,34 @@ public class KernelAccessControlController { mapNormalPageRwLbl.setText(stringBuilder.toString()); } - if (kacProvider.isInterruptPairAvailable()){ - interruptPairAvalLbl.setText("(available)"); - stringBuilder = new StringBuilder(); - for (byte b : kacProvider.getIrq0()) - stringBuilder.append(b); - stringBuilder.reverse(); - irq0Lbl.setText(stringBuilder.toString()); + for (Map.Entry entry : kacProvider.getInterruptPairs().entrySet()){ + Label no = new Label("# "+entry.getKey()); + Label irq0Lbl = new Label("irq0:"); + Label irq1Lbl = new Label("irq1:"); + Label irq0, irq1; stringBuilder = new StringBuilder(); - for (byte b : kacProvider.getIrq1()) + for (byte b : ((byte[][]) entry.getValue())[0]) stringBuilder.append(b); stringBuilder.reverse(); - irq1Lbl.setText(stringBuilder.toString()); - } - else { - interruptPairAvalLbl.setText("(not available)"); + irq0 = new Label(stringBuilder.toString()); + for (byte b : ((byte[][]) entry.getValue())[1]) + stringBuilder.append(b); + stringBuilder.reverse(); + irq1 = new Label(stringBuilder.toString()); + + no.setPadding(new Insets(5.0, 5.0, 5.0, 5.0)); + irq0Lbl.setPadding(new Insets(5.0, 5.0, 5.0, 5.0)); + irq1Lbl.setPadding(new Insets(5.0, 5.0, 5.0, 5.0)); + irq0.setPadding(new Insets(5.0, 5.0, 5.0, 5.0)); + irq1.setPadding(new Insets(5.0, 5.0, 5.0, 5.0)); + + interruptPairsPane.getChildren().addAll( + no, + new HBox(irq0Lbl, irq0), + new HBox(irq1Lbl, irq1), + new Separator() + ); } switch (kacProvider.getApplicationType()){ case 0: diff --git a/src/main/java/konogonka/Controllers/NPDM/NPDMController.java b/src/main/java/konogonka/Controllers/NPDM/NPDMController.java index 268914a..951a86f 100644 --- a/src/main/java/konogonka/Controllers/NPDM/NPDMController.java +++ b/src/main/java/konogonka/Controllers/NPDM/NPDMController.java @@ -75,14 +75,21 @@ public class NPDMController implements ITabController { acidKernelAccessControlOffsetLbl, acidKernelAccessControlSizeLbl, acidReserved2Lbl; + //ACID @FXML - private FSAccessControlController FSAccessControlTableController; + private FSAccessControlController ACIDFSAccessControlTableController; @FXML - private ServiceAccessControlController ServiceAccessControlTableController; + private ServiceAccessControlController ACIDServiceAccessControlTableController; @FXML - private KernelAccessControlController KernelAccessControlTableController; + private KernelAccessControlController ACIDKernelAccessControlTableController; + // ACI0 + @FXML + private ServiceAccessControlController ACI0ServiceAccessControlTableController; + + @FXML + private KernelAccessControlController ACI0KernelAccessControlTableController; @Override public void initialize(URL url, ResourceBundle resourceBundle) { } @@ -167,12 +174,13 @@ public class NPDMController implements ITabController { acidKernelAccessControlOffsetLbl.setText("-"); acidKernelAccessControlSizeLbl.setText("-"); acidReserved2Lbl.setText("-"); - - FSAccessControlTableController.resetTab(); - - ServiceAccessControlTableController.resetTab(); - - KernelAccessControlTableController.resetTab(); + // ACID + ACIDFSAccessControlTableController.resetTab(); + ACIDServiceAccessControlTableController.resetTab(); + ACIDKernelAccessControlTableController.resetTab(); + // ACI0 + ACI0ServiceAccessControlTableController.resetTab(); + ACI0KernelAccessControlTableController.resetTab(); } private void setData(NPDMProvider npdmProvider, File file) { if (npdmProvider == null) @@ -212,6 +220,10 @@ public class NPDMController implements ITabController { aci0KernelAccessControlOffsetLbl.setText(Integer.toString(aci0.getKernelAccessControlOffset())); aci0KernelAccessControlSizeLbl.setText(Integer.toString(aci0.getKernelAccessControlSize())); aci0Reserved3Lbl.setText(byteArrToHexString(aci0.getReserved3())); + + + ACI0ServiceAccessControlTableController.populateFields(aci0.getServiceAccessControlProvider().getCollection()); + ACI0KernelAccessControlTableController.populateFields(aci0.getKernelAccessControlProvider()); // ACID ACIDProvider acid = npdmProvider.getAcid(); acidRsa2048signatureTf.setText(byteArrToHexString(acid.getRsa2048signature())); @@ -233,8 +245,8 @@ public class NPDMController implements ITabController { acidKernelAccessControlSizeLbl.setText(Integer.toString(acid.getKernelAccessControlSize())); acidReserved2Lbl.setText(byteArrToHexString(acid.getReserved2())); - FSAccessControlTableController.populateFields(acid.getFsAccessControlProvider()); - ServiceAccessControlTableController.populateFields(acid.getServiceAccessControlProvider().getCollection()); - KernelAccessControlTableController.populateFields(acid.getKernelAccessControlProvider()); + ACIDFSAccessControlTableController.populateFields(acid.getFsAccessControlProvider()); + ACIDServiceAccessControlTableController.populateFields(acid.getServiceAccessControlProvider().getCollection()); + ACIDKernelAccessControlTableController.populateFields(acid.getKernelAccessControlProvider()); } } \ No newline at end of file diff --git a/src/main/java/konogonka/LoperConverter.java b/src/main/java/konogonka/LoperConverter.java index 2076030..3ad91da 100644 --- a/src/main/java/konogonka/LoperConverter.java +++ b/src/main/java/konogonka/LoperConverter.java @@ -30,6 +30,11 @@ public class LoperConverter { sb.append(String.format("%02x", b)); return sb.toString(); } + + public static String longToOctString(long value){ + return String.format("%64s", Long.toBinaryString( value )).replace(' ', '0'); + } + public static byte[] flip(byte[] bytes){ int size = bytes.length; byte[] ret = new byte[size]; diff --git a/src/main/java/konogonka/RainbowHexDump.java b/src/main/java/konogonka/RainbowHexDump.java index f53a6f7..226388e 100644 --- a/src/main/java/konogonka/RainbowHexDump.java +++ b/src/main/java/konogonka/RainbowHexDump.java @@ -33,4 +33,8 @@ public class RainbowHexDump { public static void octDumpInt(int value){ System.out.println(String.format("%32s", Integer.toBinaryString( value )).replace(' ', '0')+" | "+value); } + + public static void octDumpLong(long value){ + System.out.println(String.format("%64s", Long.toBinaryString( value )).replace(' ', '0')+" | "+value); + } } diff --git a/src/main/java/konogonka/Tools/NPDM/ACI0Provider.java b/src/main/java/konogonka/Tools/NPDM/ACI0Provider.java index 16973fa..e79ecfa 100644 --- a/src/main/java/konogonka/Tools/NPDM/ACI0Provider.java +++ b/src/main/java/konogonka/Tools/NPDM/ACI0Provider.java @@ -1,8 +1,10 @@ package konogonka.Tools.NPDM; +import konogonka.Tools.NPDM.ACID.KernelAccessControlProvider; +import konogonka.Tools.NPDM.ACID.ServiceAccessControlProvider; + import java.nio.charset.StandardCharsets; import java.util.Arrays; -import java.util.LinkedList; import static konogonka.LoperConverter.getLEint; @@ -19,6 +21,10 @@ public class ACI0Provider { private int kernelAccessControlSize; private byte[] reserved3; + private FSAccessHeaderProvider fsAccessHeaderProvider; + private ServiceAccessControlProvider serviceAccessControlProvider; + private KernelAccessControlProvider kernelAccessControlProvider; + public ACI0Provider(byte[] aci0bytes) throws Exception { if (aci0bytes.length < 0x40) throw new Exception("ACI0 size is too short"); @@ -33,6 +39,10 @@ public class ACI0Provider { kernelAccessControlOffset = getLEint(aci0bytes, 0x30); kernelAccessControlSize = getLEint(aci0bytes, 0x34); reserved3 = Arrays.copyOfRange(aci0bytes, 0x38, 0x40); + + fsAccessHeaderProvider = new FSAccessHeaderProvider(Arrays.copyOfRange(aci0bytes, fsAccessHeaderOffset, fsAccessHeaderOffset+fsAccessHeaderSize)); + serviceAccessControlProvider = new ServiceAccessControlProvider(Arrays.copyOfRange(aci0bytes, serviceAccessControlOffset, serviceAccessControlOffset+serviceAccessControlSize)); + kernelAccessControlProvider = new KernelAccessControlProvider(Arrays.copyOfRange(aci0bytes, kernelAccessControlOffset, kernelAccessControlOffset+kernelAccessControlSize)); } public String getMagicNum() { return magicNum; } @@ -46,4 +56,8 @@ public class ACI0Provider { public int getKernelAccessControlOffset() { return kernelAccessControlOffset; } public int getKernelAccessControlSize() { return kernelAccessControlSize; } public byte[] getReserved3() { return reserved3; } + + public FSAccessHeaderProvider getFsAccessHeaderProvider() { return fsAccessHeaderProvider; } + public ServiceAccessControlProvider getServiceAccessControlProvider() { return serviceAccessControlProvider; } + public KernelAccessControlProvider getKernelAccessControlProvider() { return kernelAccessControlProvider; } } \ No newline at end of file diff --git a/src/main/java/konogonka/Tools/NPDM/ACID/FSAccessControlProvider.java b/src/main/java/konogonka/Tools/NPDM/ACID/FSAccessControlProvider.java index e267df1..66b30d4 100644 --- a/src/main/java/konogonka/Tools/NPDM/ACID/FSAccessControlProvider.java +++ b/src/main/java/konogonka/Tools/NPDM/ACID/FSAccessControlProvider.java @@ -1,5 +1,7 @@ package konogonka.Tools.NPDM.ACID; +import konogonka.LoperConverter; + import java.util.Arrays; /** @@ -9,18 +11,18 @@ public class FSAccessControlProvider { private byte version; private byte[] padding; - private byte[] permissionsBitmask; // ??? Where each byte representing 255-long bit-bask of the thing it representing. Like 0x01 bit-mask for ApplicationInfo + private long permissionsBitmask; private byte[] reserved; public FSAccessControlProvider(byte[] bytes) { version = bytes[0]; padding = Arrays.copyOfRange(bytes, 1, 0x4); - permissionsBitmask = Arrays.copyOfRange(bytes, 0x4, 0xC); + permissionsBitmask = LoperConverter.getLElong(bytes, 0x4); reserved = Arrays.copyOfRange(bytes, 0xC, 0x2C); } public byte getVersion() { return version; } public byte[] getPadding() { return padding; } - public byte[] getPermissionsBitmask() { return permissionsBitmask; } + public long getPermissionsBitmask() { return permissionsBitmask; } public byte[] getReserved() { return reserved; } } diff --git a/src/main/java/konogonka/Tools/NPDM/ACID/KernelAccessControlProvider.java b/src/main/java/konogonka/Tools/NPDM/ACID/KernelAccessControlProvider.java index 33e7661..1237ce3 100644 --- a/src/main/java/konogonka/Tools/NPDM/ACID/KernelAccessControlProvider.java +++ b/src/main/java/konogonka/Tools/NPDM/ACID/KernelAccessControlProvider.java @@ -1,9 +1,12 @@ package konogonka.Tools.NPDM.ACID; import konogonka.LoperConverter; +import konogonka.RainbowHexDump; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedList; +import java.util.Map; /* NOTE: This implementation is extremely bad for using application as library. Use raw for own purposes. @@ -81,9 +84,7 @@ public class KernelAccessControlProvider { // MapNormalPage (RW) private byte[] mapNormalPage; // TODO: clarify is possible to have multiple // InterruptPair - private boolean interruptPairAvailable; - private byte[] irq0, - irq1; + private LinkedHashMap interruptPairs; // Number; irq0, irq2 // Application type private int applicationType; // KernelReleaseVersion @@ -97,12 +98,13 @@ public class KernelAccessControlProvider { canBeDebugged, canDebugOthers; - KernelAccessControlProvider(byte[] bytes) throws Exception{ + public KernelAccessControlProvider(byte[] bytes) throws Exception{ if (bytes.length < 4) throw new Exception("ACID-> KernelAccessControlProvider: too small size of the Kernel Access Control"); rawData = new LinkedList(); + interruptPairs = new LinkedHashMap<>(); syscallMasks = new LinkedHashMap(); mapIoOrNormalRange = new LinkedHashMap(); @@ -160,14 +162,19 @@ public class KernelAccessControlProvider { //System.out.println(); break; case INTERRUPTPAIR: - System.out.println("INTERRUPTPAIR found (must (?) appear once, please report if it's not)"); - interruptPairAvailable = true; - irq0 = new byte[10]; - irq1 = new byte[10]; + //System.out.println("INTERRUPTPAIR"); + //RainbowHexDump.octDumpInt(block); + byte[][] pair = new byte[2][]; + byte[] irq0 = new byte[10]; + byte[] irq1 = new byte[10]; + for (int k = 21; k >= 12; k--) irq0[k-12] = (byte) (block >> k & 1); for (int k = 31; k >= 22; k--) irq1[k-22] = (byte) (block >> k & 1); + pair[0] = irq0; + pair[1] = irq1; + interruptPairs.put(interruptPairs.size(), pair); break; case APPLICATIONTYPE: applicationType = block >> 14 & 0b111; @@ -193,7 +200,6 @@ public class KernelAccessControlProvider { System.out.println("UNKNOWN\t\t"+block+" "+type); } } - //System.out.println(); } private int getMinBitCnt(int value){ @@ -212,9 +218,7 @@ public class KernelAccessControlProvider { public int getKernelFlagThreadPrioLo() { return kernelFlagThreadPrioLo; } public LinkedHashMap getMapIoOrNormalRange() { return mapIoOrNormalRange; } public byte[] getMapNormalPage() { return mapNormalPage; } - public boolean isInterruptPairAvailable() { return interruptPairAvailable; } - public byte[] getIrq0() { return irq0; } - public byte[] getIrq1() { return irq1; } + public LinkedHashMap getInterruptPairs() { return interruptPairs; } public int getApplicationType() { return applicationType; } public boolean isKernelRelVersionAvailable() { return isKernelRelVersionAvailable; } public int getKernelRelVersionMajor() { return kernelRelVersionMajor; } diff --git a/src/main/java/konogonka/Tools/NPDM/FSAccessHeaderProvider.java b/src/main/java/konogonka/Tools/NPDM/FSAccessHeaderProvider.java new file mode 100644 index 0000000..4f294cf --- /dev/null +++ b/src/main/java/konogonka/Tools/NPDM/FSAccessHeaderProvider.java @@ -0,0 +1,56 @@ +package konogonka.Tools.NPDM; + +import konogonka.LoperConverter; +import konogonka.RainbowHexDump; + +import java.util.Arrays; + +/** + * For ACI0 Provider + * */ +public class FSAccessHeaderProvider { + + private byte version; + private byte[] padding; + private long permissionsBitmask; + private int dataSize; + private int contentOwnIdSectionSize; + private int dataNownerSizes; + private int saveDataOwnSectionSize; + private byte[] unknownData; + + public FSAccessHeaderProvider(byte[] bytes) { + version = bytes[0]; + padding = Arrays.copyOfRange(bytes, 1, 0x4); + permissionsBitmask = LoperConverter.getLElong(bytes, 0x4); + dataSize = LoperConverter.getLEint(bytes, 0xC); + contentOwnIdSectionSize = LoperConverter.getLEint(bytes, 0x10); + dataNownerSizes = LoperConverter.getLEint(bytes, 0x14); + saveDataOwnSectionSize = LoperConverter.getLEint(bytes, 0x18); + unknownData = Arrays.copyOfRange(bytes, 0x1C, bytes.length); + // + System.out.println("version "+version); + System.out.print("padding "); + RainbowHexDump.hexDumpUTF8(padding); + System.out.print("Permissions Bitmask "); + RainbowHexDump.octDumpLong(permissionsBitmask); + System.out.println( + "DataSize "+dataSize+"\n"+ + "Content OwnId Section Size "+contentOwnIdSectionSize+"\n"+ + "Data + owner "+dataNownerSizes+"\n"+ + "Save Data Own Section Size "+saveDataOwnSectionSize + ); + RainbowHexDump.hexDumpUTF8(Arrays.copyOfRange(bytes, 0x1C, bytes.length)); + //*/ + //reserved = Arrays.copyOfRange(bytes, 0xC, 0x2C); + } + + public byte getVersion() { return version; } + public byte[] getPadding() { return padding; } + public long getPermissionsBitmask() { return permissionsBitmask; } + public int getDataSize() { return dataSize; } + public int getContentOwnIdSectionSize() { return contentOwnIdSectionSize; } + public int getDataNownerSizes() { return dataNownerSizes; } + public int getSaveDataOwnSectionSize() { return saveDataOwnSectionSize; } + public byte[] getUnknownData() { return unknownData; } +} diff --git a/src/main/resources/FXML/NPDM/FSAccessControlTable.fxml b/src/main/resources/FXML/NPDM/FSAccessControlTable.fxml index 345dd7f..4f7ea7f 100644 --- a/src/main/resources/FXML/NPDM/FSAccessControlTable.fxml +++ b/src/main/resources/FXML/NPDM/FSAccessControlTable.fxml @@ -211,7 +211,7 @@ -