From f8b60af41ba990bd3b844eee9aed188405405e01 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Wed, 1 Feb 2023 12:43:47 +0300 Subject: [PATCH] FS drafts --- .../Controllers/PatchesController.java | 43 +++- .../Utilities/patches/AHeuristic.java | 45 ++++ .../patches/{es => }/BinToAsmPrinter.java | 47 +++- .../patches/{es => }/SimplyFind.java | 2 +- .../Utilities/patches/es/EsNcaSearchTask.java | 2 +- .../Utilities/patches/es/EsPatch.java | 10 +- .../Utilities/patches/es/EsPatchMaker.java | 6 +- .../patches/es/finders/HeuristicEs1.java | 7 +- .../patches/es/finders/HeuristicEs2.java | 7 +- .../patches/es/finders/HeuristicEs3.java | 7 +- .../patches/es/finders/HeuristicEsWizard.java | 18 +- .../patches/es/finders/IHeuristicEs.java | 45 ---- .../Utilities/patches/fs/FsNcaSearchTask.java | 50 +++++ .../Utilities/patches/fs/FsPatch.java | 208 ++++++++++++++++++ .../Utilities/patches/fs/FsPatchMaker.java | 192 ++++++++++++++++ .../patches/fs/finders/HeuristicFsExFAT1.java | 179 +++++++++++++++ .../patches/fs/finders/HeuristicFsExFAT2.java | 182 +++++++++++++++ .../patches/fs/finders/HeuristicFsFAT1.java | 176 +++++++++++++++ .../patches/fs/finders/HeuristicFsFAT2.java | 163 ++++++++++++++ .../patches/fs/finders/HeuristicFsWizard.java | 139 ++++++++++++ src/main/resources/PatchesTab.fxml | 5 +- 21 files changed, 1447 insertions(+), 86 deletions(-) create mode 100644 src/main/java/nsusbloader/Utilities/patches/AHeuristic.java rename src/main/java/nsusbloader/Utilities/patches/{es => }/BinToAsmPrinter.java (92%) rename src/main/java/nsusbloader/Utilities/patches/{es => }/SimplyFind.java (99%) delete mode 100644 src/main/java/nsusbloader/Utilities/patches/es/finders/IHeuristicEs.java create mode 100644 src/main/java/nsusbloader/Utilities/patches/fs/FsNcaSearchTask.java create mode 100644 src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java create mode 100644 src/main/java/nsusbloader/Utilities/patches/fs/FsPatchMaker.java create mode 100644 src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsExFAT1.java create mode 100644 src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsExFAT2.java create mode 100644 src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsFAT1.java create mode 100644 src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsFAT2.java create mode 100644 src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsWizard.java diff --git a/src/main/java/nsusbloader/Controllers/PatchesController.java b/src/main/java/nsusbloader/Controllers/PatchesController.java index 66aaaf7..471bbe0 100644 --- a/src/main/java/nsusbloader/Controllers/PatchesController.java +++ b/src/main/java/nsusbloader/Controllers/PatchesController.java @@ -39,20 +39,21 @@ import nsusbloader.MediatorControl; import nsusbloader.NSLDataTypes.EModule; import nsusbloader.ServiceWindow; import nsusbloader.Utilities.patches.es.EsPatchMaker; +import nsusbloader.Utilities.patches.fs.FsPatchMaker; // TODO: CLI SUPPORT public class PatchesController implements Initializable { @FXML private VBox patchesToolPane; @FXML - private Button selFwFolderBtn, selProdKeysBtn, makeEsBtn; + private Button selFwFolderBtn, selProdKeysBtn, makeEsBtn, makeFsBtn; @FXML private Label shortNameFirmwareLbl, locationFirmwareLbl, saveToLbl, shortNameKeysLbl, locationKeysLbl, statusLbl; private Thread workThread; private String previouslyOpenedPath; private ResourceBundle resourceBundle; - private Region convertRegion; + private Region convertRegionEs; @Override public void initialize(URL url, ResourceBundle resourceBundle) { @@ -70,9 +71,10 @@ public class PatchesController implements Initializable { locationKeysLbl.textProperty().addListener((observableValue, currentText, updatedText) -> shortNameKeysLbl.setText(updatedText.replaceAll(myRegexp, ""))); - convertRegion = new Region(); - convertRegion.getStyleClass().add("regionCake"); - makeEsBtn.setGraphic(convertRegion); + convertRegionEs = new Region(); + convertRegionEs.getStyleClass().add("regionCake"); + makeEsBtn.setGraphic(convertRegionEs); + //makeFsBtn.setGraphic(convertRegionEs); AppPreferences preferences = AppPreferences.getInstance(); String keysLocation = preferences.getKeysLocation(); @@ -85,6 +87,7 @@ public class PatchesController implements Initializable { saveToLbl.setText(preferences.getPatchesSaveToLocation()); //makeEsBtn.disableProperty().bind(Bindings.isEmpty(locationFirmwareLbl.textProperty())); makeEsBtn.setOnAction(actionEvent -> makeEs()); + makeFsBtn.setOnAction(actionEvent -> makeFs()); } /** @@ -174,6 +177,30 @@ public class PatchesController implements Initializable { workThread.setDaemon(true); workThread.start(); } + private void makeFs(){ + if (locationFirmwareLbl.getText().isEmpty() || locationKeysLbl.getText().isEmpty()){ + ServiceWindow.getErrorNotification(resourceBundle.getString("windowTitleError"), + resourceBundle.getString("tabPatches_ServiceWindowMessage")); + return; + } + + if (workThread != null && workThread.isAlive()) + return; + statusLbl.setText(""); + + if (MediatorControl.getInstance().getTransferActive()) { + ServiceWindow.getErrorNotification(resourceBundle.getString("windowTitleError"), + resourceBundle.getString("windowBodyPleaseStopOtherProcessFirst")); + return; + } + + FsPatchMaker fsPatchMaker = new FsPatchMaker(locationFirmwareLbl.getText(), locationKeysLbl.getText(), + saveToLbl.getText()); + workThread = new Thread(fsPatchMaker); + + workThread.setDaemon(true); + workThread.start(); + } private void interruptProcessOfPatchMaking(){ if (workThread == null || ! workThread.isAlive()) return; @@ -187,11 +214,11 @@ public class PatchesController implements Initializable { return; } - convertRegion.getStyleClass().clear(); + convertRegionEs.getStyleClass().clear(); if (isActive) { MediatorControl.getInstance().getContoller().logArea.clear(); - convertRegion.getStyleClass().add("regionStop"); + convertRegionEs.getStyleClass().add("regionStop"); makeEsBtn.setOnAction(e-> interruptProcessOfPatchMaking()); makeEsBtn.setText(resourceBundle.getString("btn_Stop")); @@ -199,7 +226,7 @@ public class PatchesController implements Initializable { makeEsBtn.getStyleClass().add("buttonStop"); } else { - convertRegion.getStyleClass().add("regionCake"); + convertRegionEs.getStyleClass().add("regionCake"); makeEsBtn.setOnAction(actionEvent -> makeEs()); makeEsBtn.setText(resourceBundle.getString("tabPatches_Btn_MakeEs")); diff --git a/src/main/java/nsusbloader/Utilities/patches/AHeuristic.java b/src/main/java/nsusbloader/Utilities/patches/AHeuristic.java new file mode 100644 index 0000000..1d1b6ea --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/patches/AHeuristic.java @@ -0,0 +1,45 @@ +/* + Copyright 2018-2022 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . + */ +package nsusbloader.Utilities.patches; +/** + * Searches instructions (via known patterns) that follows 'specific instruction' we want to patch. + * Returns offset of the pattern. Not offset of the 'specific instruction'. + * */ +public abstract class AHeuristic { + protected boolean isLDR(int expression){ return (expression >> 22 & 0x2FF) == 0x2e5; }// LDR ! Sounds like LDP, don't mess up + protected boolean isLDP(int expression){ return (expression >> 22 & 0x1F9) == 0xA1; }// LDP ! + protected boolean isCBNZ(int expression){ return (expression >> 24 & 0x7f) == 0x35; } + protected boolean isMOV(int expression){ return (expression >> 23 & 0xff) == 0xA5; } + protected boolean isTBZ(int expression){ return (expression >> 24 & 0x7f) == 0x36; } + protected boolean isLDRB_LDURB(int expression){ return (expression >> 21 & 0x7f7) == 0x1c2; } + protected boolean isMOV_REG(int expression){ return (expression & 0x7FE0FFE0) == 0x2A0003E0; } + protected boolean isB(int expression) { return (expression >> 26 & 0x3f) == 0x5; } + protected boolean isBL(int expression){ return (expression >> 26 & 0x3f) == 0x25; } + protected boolean isADD(int expression){ return (expression >> 23 & 0xff) == 0x22; } + public abstract boolean isFound(); + public abstract boolean wantLessEntropy(); + public abstract int getOffset() throws Exception; + public abstract String getDetails(); + + /** + * Should be used if wantLessEntropy() == true + * @return isFound(); + * */ + public abstract boolean setOffsetsNearby(int offsetNearby); +} diff --git a/src/main/java/nsusbloader/Utilities/patches/es/BinToAsmPrinter.java b/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java similarity index 92% rename from src/main/java/nsusbloader/Utilities/patches/es/BinToAsmPrinter.java rename to src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java index 65a8fed..36cb41f 100644 --- a/src/main/java/nsusbloader/Utilities/patches/es/BinToAsmPrinter.java +++ b/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java @@ -16,10 +16,9 @@ You should have received a copy of the GNU General Public License along with NS-USBloader. If not, see . */ -package nsusbloader.Utilities.patches.es; +package nsusbloader.Utilities.patches; import libKonogonka.Converter; -import nsusbloader.Main; import nsusbloader.NSLMain; public class BinToAsmPrinter { @@ -123,6 +122,12 @@ public class BinToAsmPrinter { } case 0xA2: return printSUBSimplified(instructionExpression, offset); + case 0xE2: + case 0x1e2: + return printCMPSimplified(instructionExpression, offset); + case 0x24: + case 0x124: + return printANDSimplified(instructionExpression, offset); } switch (instructionExpression >> 24 & 0xff) { @@ -145,6 +150,7 @@ public class BinToAsmPrinter { case 0x25: return printBLSimplified(instructionExpression, offset); } + System.out.printf("0x%x\n", (instructionExpression >> 23 & 0xff)); return printUnknownSimplified(instructionExpression, offset); } @@ -411,7 +417,7 @@ public class BinToAsmPrinter { int conditionalJumpLocation = ((instructionExpression >> 4 & 0b1111111111111111111) * 4 + offset) & 0xfffff; return String.format( - "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " B.%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n", + "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " B.%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n", offset, Integer.reverseBytes(instructionExpression), instructionExpression, getBConditionalMarker(instructionExpression & 0xf), conditionalJumpLocation, (conditionalJumpLocation + 0x100)); @@ -437,8 +443,8 @@ public class BinToAsmPrinter { private static String printMOVRegisterSimplified(int instructionExpression, int offset){ //ADD (immediate) String sfHw = (instructionExpression >> 31 & 1) == 0 ? "W" : "X"; - int Rm = instructionExpression >> 16 & 0xF; - int Rd = instructionExpression & 0xF; + int Rm = instructionExpression >> 16 & 0x1F; + int Rd = instructionExpression & 0x1F; return String.format( "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV (reg) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n", @@ -480,11 +486,40 @@ public class BinToAsmPrinter { wx, Rt, wx, Rn, imm12); } + private static String printCMPSimplified(int instructionExpression, int offset){ + String sf = (instructionExpression >> 31 == 0) ? "W" : "X"; + int Rn = instructionExpression >> 5 & 0x1F; + int conditionalJumpLocation = (instructionExpression >> 10) & 0xfff; + int LSL = (instructionExpression >> 22 & 0b1) == 1 ? 12 : 0; + return String.format( + "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMP " + ANSI_GREEN + sf + "%d," + + ANSI_BLUE + "0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ") " + ANSI_PURPLE + "LSL #%d" + ANSI_RESET + "\n", + offset, Integer.reverseBytes(instructionExpression), instructionExpression, + Rn, + conditionalJumpLocation, (conditionalJumpLocation + 0x100), + LSL); + } + + private static String printANDSimplified(int instructionExpression, int offset){ + String sf = (instructionExpression >> 31 == 0) ? "W" : "X"; + int Rn = instructionExpression & 0x1F; + int Rd = instructionExpression >> 5 & 0x1F; + int imm; + if (sf.equals("W")) + imm = instructionExpression >> 10 & 0xfff; + else + imm = instructionExpression >> 10 & 0x1fff; + + return String.format( + "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " AND " + ANSI_GREEN + sf + "%d, " + ANSI_BLUE + + sf + "%d" + ANSI_PURPLE + " # ??? 0b%s " + ANSI_RESET + "\n", + offset, Integer.reverseBytes(instructionExpression), instructionExpression, Rn, Rd, Converter.intToBinaryString(imm)); + } private static String printUnknownSimplified(int instructionExpression, int offset){ return String.format( - "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " ??? 0b"+ANSI_RESET+ Converter.intToBinaryString(Integer.reverseBytes(instructionExpression)) +"\n", + "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " ??? 0b"+ANSI_RESET+ Converter.intToBinaryString(instructionExpression) +"\n", offset, Integer.reverseBytes(instructionExpression), instructionExpression); } diff --git a/src/main/java/nsusbloader/Utilities/patches/es/SimplyFind.java b/src/main/java/nsusbloader/Utilities/patches/SimplyFind.java similarity index 99% rename from src/main/java/nsusbloader/Utilities/patches/es/SimplyFind.java rename to src/main/java/nsusbloader/Utilities/patches/SimplyFind.java index c3a648f..e16a824 100644 --- a/src/main/java/nsusbloader/Utilities/patches/es/SimplyFind.java +++ b/src/main/java/nsusbloader/Utilities/patches/SimplyFind.java @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with NS-USBloader. If not, see . */ -package nsusbloader.Utilities.patches.es; +package nsusbloader.Utilities.patches; import libKonogonka.Converter; diff --git a/src/main/java/nsusbloader/Utilities/patches/es/EsNcaSearchTask.java b/src/main/java/nsusbloader/Utilities/patches/es/EsNcaSearchTask.java index 12edaff..36b9716 100644 --- a/src/main/java/nsusbloader/Utilities/patches/es/EsNcaSearchTask.java +++ b/src/main/java/nsusbloader/Utilities/patches/es/EsNcaSearchTask.java @@ -19,7 +19,7 @@ package nsusbloader.Utilities.patches.es; import libKonogonka.Converter; -import libKonogonka.Tools.NCA.NCAProvider; +import libKonogonka.fs.NCA.NCAProvider; import java.util.List; import java.util.concurrent.Callable; diff --git a/src/main/java/nsusbloader/Utilities/patches/es/EsPatch.java b/src/main/java/nsusbloader/Utilities/patches/es/EsPatch.java index e0b2003..f513c8b 100644 --- a/src/main/java/nsusbloader/Utilities/patches/es/EsPatch.java +++ b/src/main/java/nsusbloader/Utilities/patches/es/EsPatch.java @@ -22,11 +22,12 @@ package nsusbloader.Utilities.patches.es; import libKonogonka.Converter; -import libKonogonka.Tools.NCA.NCAProvider; -import libKonogonka.Tools.NSO.NSO0Header; -import libKonogonka.Tools.NSO.NSO0Provider; +import libKonogonka.fs.NCA.NCAProvider; +import libKonogonka.fs.NSO.NSO0Header; +import libKonogonka.fs.NSO.NSO0Provider; import nsusbloader.ModelControllers.ILogPrinter; import nsusbloader.NSLDataTypes.EMsgType; +import nsusbloader.Utilities.patches.BinToAsmPrinter; import nsusbloader.Utilities.patches.es.finders.HeuristicEsWizard; import java.io.BufferedOutputStream; @@ -35,6 +36,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Arrays; public class EsPatch { @@ -113,7 +115,7 @@ public class EsPatch { handyEsPatch.put(getFooter()); try (BufferedOutputStream stream = new BufferedOutputStream( - Files.newOutputStream(new File(patchFileLocation).toPath()))){ + Files.newOutputStream(Paths.get(patchFileLocation)))){ stream.write(handyEsPatch.array()); } logPrinter.print("Patch created at "+patchFileLocation, EMsgType.PASS); diff --git a/src/main/java/nsusbloader/Utilities/patches/es/EsPatchMaker.java b/src/main/java/nsusbloader/Utilities/patches/es/EsPatchMaker.java index b28e2ec..e7d85fd 100644 --- a/src/main/java/nsusbloader/Utilities/patches/es/EsPatchMaker.java +++ b/src/main/java/nsusbloader/Utilities/patches/es/EsPatchMaker.java @@ -19,7 +19,7 @@ package nsusbloader.Utilities.patches.es; import libKonogonka.KeyChainHolder; -import libKonogonka.Tools.NCA.NCAProvider; +import libKonogonka.fs.NCA.NCAProvider; import nsusbloader.ModelControllers.CancellableRunnable; import nsusbloader.ModelControllers.ILogPrinter; import nsusbloader.ModelControllers.Log; @@ -92,6 +92,8 @@ public class EsPatchMaker extends CancellableRunnable { private void receiveFirmware() throws Exception{ logPrinter.print("Looking at firmware", EMsgType.INFO); this.firmware = new File(pathToFirmware); + if (! firmware.exists()) + throw new Exception("Firmware directory does not exist " + pathToFirmware); } private void buildKeyChainHolder() throws Exception{ logPrinter.print("Reading keys", EMsgType.INFO); @@ -174,7 +176,7 @@ public class EsPatchMaker extends CancellableRunnable { subTasks.add(task); return subTasks; } - private List getNextSet(Iterator iterator, int amount) throws Exception{ + private List getNextSet(Iterator iterator, int amount) throws Exception{ List ncas = new ArrayList<>(); for (int j = 0; j < amount; j++){ String ncaFileName = iterator.next(); diff --git a/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs1.java b/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs1.java index cfd1974..550d002 100644 --- a/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs1.java +++ b/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs1.java @@ -19,12 +19,13 @@ package nsusbloader.Utilities.patches.es.finders; import libKonogonka.Converter; -import nsusbloader.Utilities.patches.es.BinToAsmPrinter; -import nsusbloader.Utilities.patches.es.SimplyFind; +import nsusbloader.Utilities.patches.AHeuristic; +import nsusbloader.Utilities.patches.BinToAsmPrinter; +import nsusbloader.Utilities.patches.SimplyFind; import java.util.List; -class HeuristicEs1 implements IHeuristicEs { +class HeuristicEs1 extends AHeuristic { private static final String PATTERN = "1F90013128.8052"; private final List findings; diff --git a/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs2.java b/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs2.java index b3fd297..4dba546 100644 --- a/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs2.java +++ b/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs2.java @@ -19,13 +19,14 @@ package nsusbloader.Utilities.patches.es.finders; import libKonogonka.Converter; -import nsusbloader.Utilities.patches.es.BinToAsmPrinter; -import nsusbloader.Utilities.patches.es.SimplyFind; +import nsusbloader.Utilities.patches.AHeuristic; +import nsusbloader.Utilities.patches.BinToAsmPrinter; +import nsusbloader.Utilities.patches.SimplyFind; import java.util.ArrayList; import java.util.List; -class HeuristicEs2 implements IHeuristicEs { +class HeuristicEs2 extends AHeuristic { private static final String PATTERN = ".D2.52"; private List findings; diff --git a/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs3.java b/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs3.java index 8d7d5fd..728a98a 100644 --- a/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs3.java +++ b/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEs3.java @@ -19,12 +19,13 @@ package nsusbloader.Utilities.patches.es.finders; import libKonogonka.Converter; -import nsusbloader.Utilities.patches.es.BinToAsmPrinter; -import nsusbloader.Utilities.patches.es.SimplyFind; +import nsusbloader.Utilities.patches.AHeuristic; +import nsusbloader.Utilities.patches.BinToAsmPrinter; +import nsusbloader.Utilities.patches.SimplyFind; import java.util.List; -class HeuristicEs3 implements IHeuristicEs { +class HeuristicEs3 extends AHeuristic { private static final String PATTERN0 = "..FF97"; private static final String PATTERN1 = "......FF97"; // aka "E0230091..FF97"; diff --git a/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEsWizard.java b/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEsWizard.java index 5afdf60..e130ca9 100644 --- a/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEsWizard.java +++ b/src/main/java/nsusbloader/Utilities/patches/es/finders/HeuristicEsWizard.java @@ -18,14 +18,16 @@ */ package nsusbloader.Utilities.patches.es.finders; +import nsusbloader.Utilities.patches.AHeuristic; + import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class HeuristicEsWizard { - private final List all; - private final List found; - private final List wantLessEntropy; + private final List all; + private final List found; + private final List wantLessEntropy; private final StringBuilder errorsAndNotes; @@ -43,14 +45,14 @@ public class HeuristicEsWizard { ); this.found = all.stream() - .filter(IHeuristicEs::isFound) + .filter(AHeuristic::isFound) .collect(Collectors.toList()); if (found.isEmpty()) throw new Exception("Nothing found!"); this.wantLessEntropy = all.stream() - .filter(IHeuristicEs::wantLessEntropy) + .filter(AHeuristic::wantLessEntropy) .collect(Collectors.toList()); shareOffsetsWithEachOther(); @@ -61,14 +63,14 @@ public class HeuristicEsWizard { } private void shareOffsetsWithEachOther(){ - for (IHeuristicEs es : wantLessEntropy) { + for (AHeuristic es : wantLessEntropy) { if (shareWithNext(es)) return; } } - private boolean shareWithNext(IHeuristicEs es){ + private boolean shareWithNext(AHeuristic es){ try { - for (IHeuristicEs foundEs : found) { + for (AHeuristic foundEs : found) { if (es.setOffsetsNearby(foundEs.getOffset())) { found.add(es); wantLessEntropy.remove(es); diff --git a/src/main/java/nsusbloader/Utilities/patches/es/finders/IHeuristicEs.java b/src/main/java/nsusbloader/Utilities/patches/es/finders/IHeuristicEs.java deleted file mode 100644 index 944b474..0000000 --- a/src/main/java/nsusbloader/Utilities/patches/es/finders/IHeuristicEs.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright 2018-2022 Dmitry Isaenko - - This file is part of NS-USBloader. - - NS-USBloader 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. - - NS-USBloader 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 NS-USBloader. If not, see . - */ -package nsusbloader.Utilities.patches.es.finders; -/** - * Searches instructions (via known patterns) that follows 'specific instruction' we want to patch. - * Returns offset of the pattern. Not offset of the 'specific instruction'. - * */ -interface IHeuristicEs { - default boolean isLDR(int expression){ return (expression >> 22 & 0x2FF) == 0x2e5; }// LDR ! Sounds like LDP, don't mess up - default boolean isLDP(int expression){ return (expression >> 22 & 0x1F9) == 0xA1; }// LDP ! - default boolean isCBNZ(int expression){ return (expression >> 24 & 0x7f) == 0x35; } - default boolean isMOV(int expression){ return (expression >> 23 & 0xff) == 0xA5; } - default boolean isTBZ(int expression){ return (expression >> 24 & 0x7f) == 0x36; } - default boolean isLDRB_LDURB(int expression){ return (expression >> 21 & 0x7f7) == 0x1c2; } - default boolean isMOV_REG(int expression){ return (expression & 0x7FE0FFE0) == 0x2A0003E0; } - default boolean isB(int expression) { return (expression >> 26 & 0x3f) == 0x5; } - default boolean isBL(int expression){ return (expression >> 26 & 0x3f) == 0x25; } - default boolean isADD(int expression){ return (expression >> 23 & 0xff) == 0x22; } - boolean isFound(); - boolean wantLessEntropy(); - int getOffset() throws Exception; - String getDetails(); - - /** - * Should be used if wantLessEntropy() == true - * @return isFound(); - * */ - boolean setOffsetsNearby(int offsetNearby); -} diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/FsNcaSearchTask.java b/src/main/java/nsusbloader/Utilities/patches/fs/FsNcaSearchTask.java new file mode 100644 index 0000000..718a234 --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/patches/fs/FsNcaSearchTask.java @@ -0,0 +1,50 @@ +/* + Copyright 2018-2023 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . + */ +package nsusbloader.Utilities.patches.fs; + +import libKonogonka.Converter; +import libKonogonka.fs.NCA.NCAProvider; + +import java.util.List; +import java.util.concurrent.Callable; + +class FsNcaSearchTask implements Callable { + private final List ncaProviders; + + FsNcaSearchTask(List ncaProviders){ + this.ncaProviders = ncaProviders; + } + + @Override + public NCAProvider call() { + try { + for (NCAProvider ncaProvider : ncaProviders) { + String titleId = Converter.byteArrToHexStringAsLE(ncaProvider.getTitleId()); + if (titleId.equals("0100000000000819") || titleId.equals("010000000000081b")) { // eq. FAT || exFAT + return ncaProvider; + } + } + return null; + } + catch (Exception e){ + e.printStackTrace(); + return null; + } + } +} diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java b/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java new file mode 100644 index 0000000..84649ce --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java @@ -0,0 +1,208 @@ +/* + Copyright 2018-2023 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . + --- + Based on FS-AutoIPS.py patch script made by GBATemp member MrDude. + Taken from: https://gbatemp.net/threads/info-on-sha-256-hashes-on-fs-patches.581550/ + */ +package nsusbloader.Utilities.patches.fs; + +import libKonogonka.Converter; +import libKonogonka.KeyChainHolder; +import libKonogonka.RainbowDump; +import libKonogonka.fs.NCA.NCAProvider; +import libKonogonka.fs.NSO.NSO0Provider; +import libKonogonka.fs.RomFs.FileSystemEntry; +import libKonogonka.fs.RomFs.RomFsProvider; +import libKonogonka.fs.other.System2.System2Provider; +import libKonogonka.fs.other.System2.ini1.Ini1Provider; +import libKonogonka.fs.other.System2.ini1.KIP1Provider; +import libKonogonka.aesctr.InFileStreamProducer; +import nsusbloader.ModelControllers.ILogPrinter; +import nsusbloader.NSLDataTypes.EMsgType; +import nsusbloader.Utilities.patches.BinToAsmPrinter; +import nsusbloader.Utilities.patches.fs.finders.HeuristicFsWizard; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.util.Arrays; +import java.util.stream.Collectors; + +public class FsPatch { + private final NCAProvider ncaProvider; + private final String saveToLocation; + private final KeyChainHolder keyChainHolder; + private final ILogPrinter logPrinter; + + private long fwVersion; + private String patchName; + private byte[] _textSection; + private boolean filesystemTypeFat32; + + private HeuristicFsWizard wizard; + + FsPatch(NCAProvider ncaProvider, String saveToLocation, KeyChainHolder keyChainHolder, ILogPrinter logPrinter) throws Exception{ + this.ncaProvider = ncaProvider; + this.saveToLocation = saveToLocation + File.separator + + "atmosphere" + File.separator + "kip_patches" + File.separator + "fs_patches"; + this.keyChainHolder = keyChainHolder; + this.logPrinter = logPrinter; + + KIP1Provider kip1Provider = getKIP1Provider(); + getPatchName(kip1Provider); + getTextSection(kip1Provider); + checkFirmwareVersion(); + getFilesystemType(); + findAllOffsets(); + //mkDirs(); + //writeFile(); + //updatePatchesIni(); + //logPrinter.print(" == Debug information ==\n"+wizard.getDebug(), EMsgType.NULL); + } + private KIP1Provider getKIP1Provider() throws Exception{ + RomFsProvider romFsProvider = ncaProvider.getNCAContentProvider(0).getRomfs(); + + FileSystemEntry package2FSEntry = romFsProvider.getRootEntry().getContent() + .stream() + .filter(e -> e.getName().equals("nx")) + .collect(Collectors.toList()) + .get(0) + .getContent() + .stream() + .filter(e -> e.getName().equals("package2")) + .collect(Collectors.toList()) + .get(0); + InFileStreamProducer producer = romFsProvider.getStreamProducer(package2FSEntry); + System2Provider system2Provider = new System2Provider(producer, keyChainHolder); + Ini1Provider ini1Provider = system2Provider.getIni1Provider(); + + KIP1Provider kip1Provider = ini1Provider.getKip1List().stream() + .filter(provider -> provider.getHeader().getName().startsWith("FS")) + .collect(Collectors.toList()) + .get(0); + + if (kip1Provider == null) + throw new Exception("No FS KIP1"); + return kip1Provider; + } + private void getPatchName(KIP1Provider kip1Provider) throws Exception{ + int kip1EncryptedSize = (int) kip1Provider.getSize(); + byte[] kip1EncryptedRaw = new byte[kip1EncryptedSize]; + + try (BufferedInputStream kip1ProviderStream = kip1Provider.getStreamProducer().produce()) { + if (kip1EncryptedSize != kip1ProviderStream.read(kip1EncryptedRaw)) + throw new Exception("Unencrypted FS KIP1 read failure"); + } + + byte[] sha256ofKip1 = MessageDigest.getInstance("SHA-256").digest(kip1EncryptedRaw); + patchName = Converter.byteArrToHexStringAsLE(sha256ofKip1, true) + ".ips"; + } + private void getTextSection(KIP1Provider kip1Provider) throws Exception{ + _textSection = kip1Provider.getAsDecompressed().getTextRaw(); + } + private void checkFirmwareVersion() throws Exception{ + final byte[] byteSdkVersion = ncaProvider.getSdkVersion(); + fwVersion = Long.parseLong(""+byteSdkVersion[3] + byteSdkVersion[2] + byteSdkVersion[1] + byteSdkVersion[0]); + logPrinter.print("Internal firmware version: " + + byteSdkVersion[3] +"."+byteSdkVersion[2] +"."+byteSdkVersion[1] +"."+byteSdkVersion[0], EMsgType.INFO); + System.out.println("FW "+byteSdkVersion[3] +"."+byteSdkVersion[2] +"."+byteSdkVersion[1] +"."+byteSdkVersion[0]); // TODO:REMOVE! + if (fwVersion < 9300) + logPrinter.print("WARNING! FIRMWARES VERSIONS BEFORE 9.0.0 ARE NOT SUPPORTED! " + + "USING PRODUCED ES PATCHES (IF ANY) COULD BREAK SOMETHING! IT'S NEVER BEEN TESTED!", EMsgType.WARNING); + } + private void getFilesystemType(){ + String titleId = Converter.byteArrToHexStringAsLE(ncaProvider.getTitleId()); + filesystemTypeFat32 = titleId.equals("0100000000000819"); + } + private void findAllOffsets() throws Exception{ + // TODO: FIX, IMPLEMENT, DEPLOY + this.wizard = new HeuristicFsWizard(fwVersion, _textSection, filesystemTypeFat32); + String errorsAndNotes = wizard.getErrorsAndNotes(); + if (errorsAndNotes.length() > 0) + logPrinter.print(errorsAndNotes, EMsgType.WARNING); + } + private void mkDirs(){ + File parentFolder = new File(saveToLocation); + parentFolder.mkdirs(); + } + + private void writeFile() throws Exception{ + String patchFileLocation = saveToLocation + File.separator + patchName; // THIS IS GOOD + int offset1 = wizard.getOffset1(); + + ByteBuffer handyFsPatch = ByteBuffer.allocate(0x23).order(ByteOrder.LITTLE_ENDIAN); + handyFsPatch.put(getHeader()); + // TODO: FIX, UPDATE + if (offset1 > 0) { + logPrinter.print("Patch component 1 will be used", EMsgType.PASS); + handyFsPatch.put(getPatch1(offset1)); + } + handyFsPatch.put(getFooter()); + + try (BufferedOutputStream stream = new BufferedOutputStream( + Files.newOutputStream(Paths.get(patchFileLocation)))){ + stream.write(handyFsPatch.array()); + } + logPrinter.print("Patch created at "+patchFileLocation, EMsgType.PASS); + } + private byte[] getHeader(){ + return "PATCH".getBytes(StandardCharsets.US_ASCII); + } + private byte[] getFooter(){ + return "EOF".getBytes(StandardCharsets.US_ASCII); + } + + private byte[] getPatch1(int offset) throws Exception{ + int requiredInstructionOffsetInternal = offset - 4; + int requiredInstructionOffsetReal = requiredInstructionOffsetInternal + 0x100; + int instructionExpression = Converter.getLEint(_textSection, requiredInstructionOffsetInternal); + int patch = ((0x14 << 24) | (instructionExpression >> 5) & 0x7FFFF); + + logPrinter.print(BinToAsmPrinter.printSimplified(patch, requiredInstructionOffsetInternal), EMsgType.NULL); + + // Somehow IPS patches uses offsets written as big_endian (0.o) and bytes dat should be patched as LE. + ByteBuffer prePatch = ByteBuffer.allocate(10).order(ByteOrder.BIG_ENDIAN) + .putInt(requiredInstructionOffsetReal) + .putShort((short) 4) + .putInt(Integer.reverseBytes(patch)); + + return Arrays.copyOfRange(prePatch.array(), 1, 10); + } + private byte[] getPatch2(int offset) throws Exception{ + int requiredInstructionOffsetInternal = offset - 4; + int requiredInstructionOffsetReal = requiredInstructionOffsetInternal + 0x100; + int instructionExpression = Converter.getLEint(_textSection, requiredInstructionOffsetInternal); + int patch = ((0x14 << 24) | (instructionExpression >> 5) & 0x7FFFF); + + logPrinter.print(BinToAsmPrinter.printSimplified(patch, requiredInstructionOffsetInternal), EMsgType.NULL); + + // Somehow IPS patches uses offsets written as big_endian (0.o) and bytes dat should be patched as LE. + ByteBuffer prePatch = ByteBuffer.allocate(10).order(ByteOrder.BIG_ENDIAN) + .putInt(requiredInstructionOffsetReal) + .putShort((short) 4) + .putInt(Integer.reverseBytes(patch)); + + return Arrays.copyOfRange(prePatch.array(), 1, 10); + } +} diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/FsPatchMaker.java b/src/main/java/nsusbloader/Utilities/patches/fs/FsPatchMaker.java new file mode 100644 index 0000000..f33c4fa --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/patches/fs/FsPatchMaker.java @@ -0,0 +1,192 @@ +/* + Copyright 2018-2022 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . + */ +package nsusbloader.Utilities.patches.fs; + +import libKonogonka.KeyChainHolder; +import libKonogonka.fs.NCA.NCAProvider; +import nsusbloader.ModelControllers.CancellableRunnable; +import nsusbloader.ModelControllers.ILogPrinter; +import nsusbloader.NSLDataTypes.EMsgType; +import nsusbloader.NSLDataTypes.EFileStatus; + +import java.io.File; +import java.util.*; +import java.util.concurrent.*; + +public class FsPatchMaker extends CancellableRunnable { + private int THREADS_POOL_SIZE = 4; + private final ILogPrinter logPrinter; + private final String pathToFirmware; + private final String pathToKeysFile; + private final String saveTo; + + private File firmware; + private KeyChainHolder keyChainHolder; + private ExecutorService executorService; + private List ncaFilesList; // inside the folder + + private boolean oneLinerStatus = false; + + public FsPatchMaker(String pathToFirmware, String pathToKeysFile, String saveTo){ + //this.logPrinter = Log.getPrinter(EModule.PATCHES); //TODO: UNCOMMENT + + this.logPrinter = new ILogPrinter() { + @Override + public void print(String message, EMsgType type) throws InterruptedException {} + @Override + public void updateProgress(Double value) throws InterruptedException {} + @Override + public void update(HashMap nspMap, EFileStatus status) {} + @Override + public void update(File file, EFileStatus status) {} + @Override + public void updateOneLinerStatus(boolean status) {} + @Override + public void close() {} + }; + // */ + this.pathToFirmware = pathToFirmware; + this.pathToKeysFile = pathToKeysFile; + this.saveTo = saveTo; + } + + @Override + public void run() { + try { + logPrinter.print("..:: Make FS Patches ::..", EMsgType.INFO); + receiveFirmware(); + buildKeyChainHolder(); + receiveNcaFileNamesList(); + adjustThreadsPoolSize(); + createPool(); + executePool(); + } + catch (Exception e){ + e.printStackTrace(); + try{ + logPrinter.print(e.getMessage(), EMsgType.FAIL); + } catch (Exception ignore){} + } + finally { + logPrinter.updateOneLinerStatus(oneLinerStatus); + logPrinter.close(); + } + } + private void receiveFirmware() throws Exception{ + logPrinter.print("Looking at firmware", EMsgType.INFO); + this.firmware = new File(pathToFirmware); + if (! firmware.exists()) + throw new Exception("Firmware directory does not exist " + pathToFirmware); + } + private void buildKeyChainHolder() throws Exception{ + logPrinter.print("Reading keys", EMsgType.INFO); + this.keyChainHolder = new KeyChainHolder(pathToKeysFile, null); + } + private void receiveNcaFileNamesList() throws Exception{ + logPrinter.print("Collecting NCA files", EMsgType.INFO); + String[] fileNamesArray = firmware.list( + (File directory, String file) -> ( ! file.endsWith(".cnmt.nca") && file.endsWith(".nca"))); + ncaFilesList = Arrays.asList(Objects.requireNonNull(fileNamesArray)); + if (ncaFilesList.size() == 0) + throw new Exception("No NCA files found in firmware folder"); + } + private void adjustThreadsPoolSize(){ + if (ncaFilesList.size() < 4) + THREADS_POOL_SIZE = ncaFilesList.size(); + } + + private void createPool() throws Exception{ + logPrinter.print("Creating sub-tasks pool", EMsgType.INFO); + this.executorService = Executors.newFixedThreadPool( + THREADS_POOL_SIZE, + runnable -> { + Thread thread = new Thread(runnable); + thread.setDaemon(true); + return thread; + }); + } + + private void executePool() throws Exception{ //TODO: FIX. Exceptions thrown only by logPrinter + try { + logPrinter.print("Executing sub-tasks pool", EMsgType.INFO); + List> futuresResults = executorService.invokeAll(getSubTasksCollection()); + int counter = 0; + for (Future future : futuresResults){ + NCAProvider ncaProvider = future.get(); + if (ncaProvider != null) { + makePatches(ncaProvider); + if (++counter > 1) + break; + } + } + executorService.shutdown(); + } + catch (InterruptedException ie){ + executorService.shutdownNow(); + boolean interruptedSuccessfully = false; + try { + interruptedSuccessfully = executorService.awaitTermination(20, TimeUnit.SECONDS); + } + catch (InterruptedException awaitInterrupt){ + logPrinter.print("Force interrupting task...", EMsgType.WARNING); + } + logPrinter.print("Task interrupted "+(interruptedSuccessfully?"successfully":"with some issues"), EMsgType.WARNING); + } + catch (Exception e){ + e.printStackTrace(); + logPrinter.print("Task failed: "+e.getMessage(), EMsgType.FAIL); + } + } + + private void makePatches(NCAProvider ncaProvider) throws Exception{ + logPrinter.print(String.format("File found: .."+File.separator+"%s"+File.separator+"%s", + ncaProvider.getFile().getParentFile().getName(), ncaProvider.getFile().getName()) + , EMsgType.INFO); + //TODO : FIX; IMPLEMENT; DEPLOY ;) + new FsPatch(ncaProvider, saveTo, keyChainHolder, logPrinter); + oneLinerStatus = true; + } + private List> getSubTasksCollection() throws Exception{ + logPrinter.print("Forming sub-tasks collection", EMsgType.INFO); + List> subTasks = new ArrayList<>(); + + int ncaPerThreadAmount = ncaFilesList.size() / THREADS_POOL_SIZE; + Iterator iterator = ncaFilesList.listIterator(); + + for (int i = 1; i < THREADS_POOL_SIZE; i++){ + Callable task = new FsNcaSearchTask(getNextSet(iterator, ncaPerThreadAmount)); + subTasks.add(task); + } + + Callable task = new FsNcaSearchTask(getNextSet(iterator, + ncaFilesList.size() % THREADS_POOL_SIZE == 0 ? ncaPerThreadAmount : ncaPerThreadAmount+1)); + subTasks.add(task); + return subTasks; + } + private List getNextSet(Iterator iterator, int amount) throws Exception{ + List ncas = new ArrayList<>(); + for (int j = 0; j < amount; j++){ + String ncaFileName = iterator.next(); + File nca = new File(firmware.getAbsolutePath()+File.separator+ncaFileName); + NCAProvider provider = new NCAProvider(nca, keyChainHolder.getRawKeySet()); + ncas.add(provider); + } + return ncas; + } +} \ No newline at end of file diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsExFAT1.java b/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsExFAT1.java new file mode 100644 index 0000000..148d3e5 --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsExFAT1.java @@ -0,0 +1,179 @@ +/* + Copyright 2018-2023 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . + */ +package nsusbloader.Utilities.patches.fs.finders; + +import libKonogonka.Converter; +import nsusbloader.Utilities.patches.AHeuristic; +import nsusbloader.Utilities.patches.BinToAsmPrinter; +import nsusbloader.Utilities.patches.SimplyFind; + +import java.util.List; + +class HeuristicFsExFAT1 extends AHeuristic { + private static final String PATTERN0 = ".1e42b91fc14271"; + private static final String PATTERN1 = "9408...1F05.....54"; + + private final List findings; + private final byte[] where; + + HeuristicFsExFAT1(long fwVersion, byte[] where){ + this.where = where; + String pattern = getPattern(fwVersion); + SimplyFind simplyfind = new SimplyFind(pattern, where); + this.findings = simplyfind.getResults(); + for (Integer find : findings) + System.out.println(getDetails(find)); +/* FIXME + this.findings.removeIf(this::dropStep1); + if(findings.size() < 2) + return; + + this.findings.removeIf(this::dropStep2); + if(findings.size() < 2) + return; + + this.findings.removeIf(this::dropStep3); + */ + } + private String getPattern(long fwVersion){ + if (fwVersion < 15300) // & fwVersion >= 9300 + return PATTERN0; + return PATTERN1; + } + // Let's focus on CBZ-ONLY statements + private boolean dropStep1(int offsetOfPatternFound){ + return ((where[offsetOfPatternFound - 1] & (byte) 0b01111111) != 0x34); + } + + private boolean dropStep2(int offsetOfPatternFound){ + int conditionalJumpLocation = getCBZConditionalJumpLocation(offsetOfPatternFound - 4); + + int afterJumpSecondExpressions = Converter.getLEint(where, conditionalJumpLocation); + int afterJumpThirdExpressions = Converter.getLEint(where, conditionalJumpLocation+4); + // Check first is 'MOV'; second is 'B' + return (! isMOV_REG(afterJumpSecondExpressions)) || ! isB(afterJumpThirdExpressions); + } + + private boolean dropStep3(int offsetOfPatternFound){ + int conditionalJumpLocation = getCBZConditionalJumpLocation(offsetOfPatternFound-4); + int afterJumpSecondExpressions = Converter.getLEint(where, conditionalJumpLocation+4); + int secondPairConditionalJumpLocation = ((afterJumpSecondExpressions & 0x3ffffff) * 4 + (conditionalJumpLocation+4)) & 0xfffff; + + int thirdExpressionsPairElement1 = Converter.getLEint(where, secondPairConditionalJumpLocation); + int thirdExpressionsPairElement2 = Converter.getLEint(where, secondPairConditionalJumpLocation+4); + // Check first is 'ADD'; second is 'BL' + return (! isADD(thirdExpressionsPairElement1)) || (! isBL(thirdExpressionsPairElement2)); + } + + private int getCBZConditionalJumpLocation(int cbzOffsetInternal){ + int cbzExpression = Converter.getLEint(where, cbzOffsetInternal); + return ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff; + } + + @Override + public boolean isFound(){ + return findings.size() == 1; + } + + @Override + public boolean wantLessEntropy(){ + return findings.size() > 1; + } + + @Override + public int getOffset() throws Exception{ + if(findings.isEmpty()) + throw new Exception("Nothing found"); + if (findings.size() > 1) + throw new Exception("Too many offsets"); + return findings.get(0); + } + + @Override + public boolean setOffsetsNearby(int offsetNearby) { + findings.removeIf(offset -> { + if (offset > offsetNearby) + return ! (offset < offsetNearby - 0xffff); + return ! (offset > offsetNearby - 0xffff); + }); + return isFound(); + } + + public String getDetails(Integer value){ + StringBuilder builder = new StringBuilder(); + int cbzOffsetInternal = value - 4; + int cbzExpression = Converter.getLEint(where, cbzOffsetInternal); + int conditionalJumpLocation = ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff; + + int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation); + int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation+4); + + builder.append(BinToAsmPrinter.printSimplified(cbzExpression, cbzOffsetInternal)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+4), cbzOffsetInternal+4)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+8), cbzOffsetInternal+8)); + builder.append("...\n"); + + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation)); + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation+4)); + + if (((secondExpressionsPairElement2 >> 26 & 0b111111) == 0x5)){ + builder.append("...\n"); + int conditionalJumpLocation2 = ((secondExpressionsPairElement2 & 0x3ffffff) * 4 + (conditionalJumpLocation+4)) & 0xfffff; + + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, conditionalJumpLocation2), conditionalJumpLocation2)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, conditionalJumpLocation2+4), conditionalJumpLocation2+4)); + + } + else { + builder.append("NO CONDITIONAL JUMP ON 2nd iteration (HeuristicEs3)"); + } + return builder.toString(); + } + @Override + public String getDetails(){ + StringBuilder builder = new StringBuilder(); + int cbzOffsetInternal = findings.get(0) - 4; + int cbzExpression = Converter.getLEint(where, cbzOffsetInternal); + int conditionalJumpLocation = ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff; + + int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation); + int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation+4); + + builder.append(BinToAsmPrinter.printSimplified(cbzExpression, cbzOffsetInternal)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+4), cbzOffsetInternal+4)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+8), cbzOffsetInternal+8)); + builder.append("...\n"); + + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation)); + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation+4)); + + if (((secondExpressionsPairElement2 >> 26 & 0b111111) == 0x5)){ + builder.append("...\n"); + int conditionalJumpLocation2 = ((secondExpressionsPairElement2 & 0x3ffffff) * 4 + (conditionalJumpLocation+4)) & 0xfffff; + + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, conditionalJumpLocation2), conditionalJumpLocation2)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, conditionalJumpLocation2+4), conditionalJumpLocation2+4)); + + } + else { + builder.append("NO CONDITIONAL JUMP ON 2nd iteration (HeuristicEs3)"); + } + return builder.toString(); + } +} diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsExFAT2.java b/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsExFAT2.java new file mode 100644 index 0000000..5463a6e --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsExFAT2.java @@ -0,0 +1,182 @@ +/* + Copyright 2018-2023 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . + */ +package nsusbloader.Utilities.patches.fs.finders; + +import libKonogonka.Converter; +import nsusbloader.Utilities.patches.AHeuristic; +import nsusbloader.Utilities.patches.BinToAsmPrinter; +import nsusbloader.Utilities.patches.SimplyFind; + +import java.util.List; + +class HeuristicFsExFAT2 extends AHeuristic { + private static final String PATTERN0 = ".94081C00121F05007181000054"; + private static final String PATTERN1 = "003688...1F"; + + private final List findings; + private final byte[] where; + + HeuristicFsExFAT2(long fwVersion, byte[] where){ + this.where = where; + String pattern = getPattern(fwVersion); + SimplyFind simplyfind = new SimplyFind(pattern, where); + this.findings = simplyfind.getResults(); + + for (Integer find : findings) + System.out.println(getDetails(find)); + /* FIXME + this.findings.removeIf(this::dropStep1); + if(findings.size() < 2) + return; + + this.findings.removeIf(this::dropStep2); + if(findings.size() < 2) + return; + + this.findings.removeIf(this::dropStep3); + + */ + } + private String getPattern(long fwVersion){ + if (fwVersion < 15300) // & fwVersion >= 9300 + return PATTERN0; + return PATTERN1; + } + // Let's focus on CBZ-ONLY statements + private boolean dropStep1(int offsetOfPatternFound){ + return ((where[offsetOfPatternFound - 1] & (byte) 0b01111111) != 0x34); + } + + private boolean dropStep2(int offsetOfPatternFound){ + int conditionalJumpLocation = getCBZConditionalJumpLocation(offsetOfPatternFound - 4); + + int afterJumpSecondExpressions = Converter.getLEint(where, conditionalJumpLocation); + int afterJumpThirdExpressions = Converter.getLEint(where, conditionalJumpLocation+4); + // Check first is 'MOV'; second is 'B' + return (! isMOV_REG(afterJumpSecondExpressions)) || ! isB(afterJumpThirdExpressions); + } + + private boolean dropStep3(int offsetOfPatternFound){ + int conditionalJumpLocation = getCBZConditionalJumpLocation(offsetOfPatternFound-4); + int afterJumpSecondExpressions = Converter.getLEint(where, conditionalJumpLocation+4); + int secondPairConditionalJumpLocation = ((afterJumpSecondExpressions & 0x3ffffff) * 4 + (conditionalJumpLocation+4)) & 0xfffff; + + int thirdExpressionsPairElement1 = Converter.getLEint(where, secondPairConditionalJumpLocation); + int thirdExpressionsPairElement2 = Converter.getLEint(where, secondPairConditionalJumpLocation+4); + // Check first is 'ADD'; second is 'BL' + return (! isADD(thirdExpressionsPairElement1)) || (! isBL(thirdExpressionsPairElement2)); + } + + private int getCBZConditionalJumpLocation(int cbzOffsetInternal){ + int cbzExpression = Converter.getLEint(where, cbzOffsetInternal); + return ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff; + } + + @Override + public boolean isFound(){ + return findings.size() == 1; + } + + @Override + public boolean wantLessEntropy(){ + return findings.size() > 1; + } + + @Override + public int getOffset() throws Exception{ + if(findings.isEmpty()) + throw new Exception("Nothing found"); + if (findings.size() > 1) + throw new Exception("Too many offsets"); + return findings.get(0); + } + + @Override + public boolean setOffsetsNearby(int offsetNearby) { + findings.removeIf(offset -> { + if (offset > offsetNearby) + return ! (offset < offsetNearby - 0xffff); + return ! (offset > offsetNearby - 0xffff); + }); + return isFound(); + } + + public String getDetails(Integer value){ + StringBuilder builder = new StringBuilder(); + int cbzOffsetInternal = value - 4; + int cbzExpression = Converter.getLEint(where, cbzOffsetInternal); + int conditionalJumpLocation = ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff; + + int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation); + int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation+4); + + builder.append(BinToAsmPrinter.printSimplified(cbzExpression, cbzOffsetInternal)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+4), cbzOffsetInternal+4)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+8), cbzOffsetInternal+8)); + builder.append("...\n"); + + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation)); + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation+4)); + + if (((secondExpressionsPairElement2 >> 26 & 0b111111) == 0x5)){ + builder.append("...\n"); + int conditionalJumpLocation2 = ((secondExpressionsPairElement2 & 0x3ffffff) * 4 + (conditionalJumpLocation+4)) & 0xfffff; + + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, conditionalJumpLocation2), conditionalJumpLocation2)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, conditionalJumpLocation2+4), conditionalJumpLocation2+4)); + + } + else { + builder.append("NO CONDITIONAL JUMP ON 2nd iteration (HeuristicEs3)"); + } + return builder.toString(); + } + + @Override + public String getDetails(){ + StringBuilder builder = new StringBuilder(); + int cbzOffsetInternal = findings.get(0) - 4; + int cbzExpression = Converter.getLEint(where, cbzOffsetInternal); + int conditionalJumpLocation = ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff; + + int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation); + int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation+4); + + builder.append(BinToAsmPrinter.printSimplified(cbzExpression, cbzOffsetInternal)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+4), cbzOffsetInternal+4)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+8), cbzOffsetInternal+8)); + builder.append("...\n"); + + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation)); + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation+4)); + + if (((secondExpressionsPairElement2 >> 26 & 0b111111) == 0x5)){ + builder.append("...\n"); + int conditionalJumpLocation2 = ((secondExpressionsPairElement2 & 0x3ffffff) * 4 + (conditionalJumpLocation+4)) & 0xfffff; + + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, conditionalJumpLocation2), conditionalJumpLocation2)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, conditionalJumpLocation2+4), conditionalJumpLocation2+4)); + + } + else { + builder.append("NO CONDITIONAL JUMP ON 2nd iteration (HeuristicEs3)"); + } + return builder.toString(); + } +} diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsFAT1.java b/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsFAT1.java new file mode 100644 index 0000000..bae72b3 --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsFAT1.java @@ -0,0 +1,176 @@ +/* + Copyright 2018-2023 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . + */ +package nsusbloader.Utilities.patches.fs.finders; + +import libKonogonka.Converter; +import nsusbloader.Utilities.patches.AHeuristic; +import nsusbloader.Utilities.patches.BinToAsmPrinter; +import nsusbloader.Utilities.patches.SimplyFind; + +import java.util.ArrayList; +import java.util.List; + +class HeuristicFsFAT1 extends AHeuristic { + private static final String PATTERN0 = ".1e42b91fc14271"; + private static final String PATTERN1 = "...9408...1F05.....54"; // ...94 081C0012 1F050071 4101 +/* + 710006eba0 c0 02 40 f9 ldr x0 , [ x22 ] + 710006eba4 5c c9 02 94 bl FUN_7100121114 undefined FUN_7100121114() + 710006eba8 08 1c 00 12 and w8 , w0 , # 0xff + */ + private final List findings; + private final byte[] where; + + HeuristicFsFAT1(long fwVersion, byte[] where){ + this.where = where; + String pattern = getPattern(fwVersion); + SimplyFind simplyfind = new SimplyFind(pattern, where); + List temporary = simplyfind.getResults(); + if (fwVersion >= 15300){ + this.findings = new ArrayList<>(); + temporary.forEach(var -> findings.add(var + 4)); + } + else + findings = temporary; + + System.out.println("\t\tFAT32 # 1 +++++++++++++++++++++++++++++++"); + for (Integer find : findings) { + System.out.println(getDetails(find)); + System.out.println("------------------------------------------------------------------"); + } +/* FIXME + this.findings.removeIf(this::dropStep1); + if(findings.size() < 2) + return; + + this.findings.removeIf(this::dropStep2); + if(findings.size() < 2) + return; + + this.findings.removeIf(this::dropStep3); + */ + } + private String getPattern(long fwVersion){ + if (fwVersion < 15300) // & fwVersion >= 9300 + return PATTERN0; + return PATTERN1; + } + // Let's focus on CBZ-ONLY statements + private boolean dropStep1(int offsetOfPatternFound){ + return ((where[offsetOfPatternFound - 1] & (byte) 0b01111111) != 0x34); + } + + private boolean dropStep2(int offsetOfPatternFound){ + int conditionalJumpLocation = getCBZConditionalJumpLocation(offsetOfPatternFound - 4); + + int afterJumpSecondExpressions = Converter.getLEint(where, conditionalJumpLocation); + int afterJumpThirdExpressions = Converter.getLEint(where, conditionalJumpLocation+4); + // Check first is 'MOV'; second is 'B' + return (! isMOV_REG(afterJumpSecondExpressions)) || ! isB(afterJumpThirdExpressions); + } + + private boolean dropStep3(int offsetOfPatternFound){ + int conditionalJumpLocation = getCBZConditionalJumpLocation(offsetOfPatternFound-4); + int afterJumpSecondExpressions = Converter.getLEint(where, conditionalJumpLocation+4); + int secondPairConditionalJumpLocation = ((afterJumpSecondExpressions & 0x3ffffff) * 4 + (conditionalJumpLocation+4)) & 0xfffff; + + int thirdExpressionsPairElement1 = Converter.getLEint(where, secondPairConditionalJumpLocation); + int thirdExpressionsPairElement2 = Converter.getLEint(where, secondPairConditionalJumpLocation+4); + // Check first is 'ADD'; second is 'BL' + return (! isADD(thirdExpressionsPairElement1)) || (! isBL(thirdExpressionsPairElement2)); + } + + private int getCBZConditionalJumpLocation(int cbzOffsetInternal){ + int cbzExpression = Converter.getLEint(where, cbzOffsetInternal); + return ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff; + } + + @Override + public boolean isFound(){ + return findings.size() == 1; + } + + @Override + public boolean wantLessEntropy(){ + return findings.size() > 1; + } + + @Override + public int getOffset() throws Exception{ + if(findings.isEmpty()) + throw new Exception("Nothing found"); + if (findings.size() > 1) + throw new Exception("Too many offsets"); + return findings.get(0); + } + + @Override + public boolean setOffsetsNearby(int offsetNearby) { + findings.removeIf(offset -> { + if (offset > offsetNearby) + return ! (offset < offsetNearby - 0xffff); + return ! (offset > offsetNearby - 0xffff); + }); + return isFound(); + } + + public String getDetails(Integer value){ + int firstOffsetInternal = value - 4; + int firstExpression = Converter.getLEint(where, firstOffsetInternal); + int conditionalJumpLocation = ((firstExpression >> 5 & 0x7FFFF) * 4 + firstOffsetInternal) & 0xfffff; + + int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation); + int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation+4); + + StringBuilder builder = new StringBuilder(); + //builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, firstOffsetInternal-4*11), firstOffsetInternal-4*11)); + //builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, firstOffsetInternal-4*10), firstOffsetInternal-4*10)); + //builder.append("^ ^ ^ ...\n"); + builder.append(BinToAsmPrinter.printSimplified(firstExpression, firstOffsetInternal)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, firstOffsetInternal+4), firstOffsetInternal+4)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, firstOffsetInternal+8), firstOffsetInternal+8)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, firstOffsetInternal+12), firstOffsetInternal+12)); + builder.append("...\n"); + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation)); + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation+4)); + + return builder.toString(); + } + @Override + public String getDetails(){ + int firstOffsetInternal = findings.get(0) - 4; + int firstExpression = Converter.getLEint(where, firstOffsetInternal); + int conditionalJumpLocation = ((firstExpression >> 5 & 0x7FFFF) * 4 + firstOffsetInternal) & 0xfffff; + + int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation); + int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation+4); + + return BinToAsmPrinter.printSimplified(Converter.getLEint(where, firstOffsetInternal - 4 * 11), firstOffsetInternal - 4 * 11) + + BinToAsmPrinter.printSimplified(Converter.getLEint(where, firstOffsetInternal - 4 * 10), firstOffsetInternal - 4 * 10) + + "^ ^ ^ ...\n" + + BinToAsmPrinter.printSimplified(Converter.getLEint(where, firstOffsetInternal - 4 * 2), firstOffsetInternal - 4 * 2) + + "^ ^ ^ ...\n" + + BinToAsmPrinter.printSimplified(firstExpression, firstOffsetInternal) + + BinToAsmPrinter.printSimplified(Converter.getLEint(where, firstOffsetInternal + 4), firstOffsetInternal + 4) + + BinToAsmPrinter.printSimplified(Converter.getLEint(where, firstOffsetInternal + 8), firstOffsetInternal + 8) + + "...\n" + + BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation) + + BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation + 4); + } +} diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsFAT2.java b/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsFAT2.java new file mode 100644 index 0000000..c265a31 --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsFAT2.java @@ -0,0 +1,163 @@ +/* + Copyright 2018-2023 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . + */ +package nsusbloader.Utilities.patches.fs.finders; + +import libKonogonka.Converter; +import nsusbloader.Utilities.patches.AHeuristic; +import nsusbloader.Utilities.patches.BinToAsmPrinter; +import nsusbloader.Utilities.patches.SimplyFind; + +import java.util.List; + +class HeuristicFsFAT2 extends AHeuristic { + private static final String PATTERN0 = "...94081C00121F05007181000054"; + private static final String PATTERN1 = "..003688...1F"; + + private final List findings; + private final byte[] where; + + HeuristicFsFAT2(long fwVersion, byte[] where){ + this.where = where; + String pattern = getPattern(fwVersion); + SimplyFind simplyfind = new SimplyFind(pattern, where); + this.findings = simplyfind.getResults(); + /* + System.out.println("\t\tFAT32 # 2 +++++++++++++++++++++++++++++++"); + for (Integer find : findings) { + System.out.println(getDetails(find)); + System.out.println("------------------------------------------------------------------"); + } + /* FIXME + this.findings.removeIf(this::dropStep1); + if(findings.size() < 2) + return; + + this.findings.removeIf(this::dropStep2); + if(findings.size() < 2) + return; + + this.findings.removeIf(this::dropStep3); + + */ + } + private String getPattern(long fwVersion){ + if (fwVersion < 15300) // & fwVersion >= 9300 + return PATTERN0; + return PATTERN1; + } + // Let's focus on CBZ-ONLY statements + private boolean dropStep1(int offsetOfPatternFound){ + return ((where[offsetOfPatternFound - 1] & (byte) 0b01111111) != 0x34); + } + + private boolean dropStep2(int offsetOfPatternFound){ + int conditionalJumpLocation = getCBZConditionalJumpLocation(offsetOfPatternFound - 4); + + int afterJumpSecondExpressions = Converter.getLEint(where, conditionalJumpLocation); + int afterJumpThirdExpressions = Converter.getLEint(where, conditionalJumpLocation+4); + // Check first is 'MOV'; second is 'B' + return (! isMOV_REG(afterJumpSecondExpressions)) || ! isB(afterJumpThirdExpressions); + } + + private boolean dropStep3(int offsetOfPatternFound){ + int conditionalJumpLocation = getCBZConditionalJumpLocation(offsetOfPatternFound-4); + int afterJumpSecondExpressions = Converter.getLEint(where, conditionalJumpLocation+4); + int secondPairConditionalJumpLocation = ((afterJumpSecondExpressions & 0x3ffffff) * 4 + (conditionalJumpLocation+4)) & 0xfffff; + + int thirdExpressionsPairElement1 = Converter.getLEint(where, secondPairConditionalJumpLocation); + int thirdExpressionsPairElement2 = Converter.getLEint(where, secondPairConditionalJumpLocation+4); + // Check first is 'ADD'; second is 'BL' + return (! isADD(thirdExpressionsPairElement1)) || (! isBL(thirdExpressionsPairElement2)); + } + + private int getCBZConditionalJumpLocation(int cbzOffsetInternal){ + int cbzExpression = Converter.getLEint(where, cbzOffsetInternal); + return ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff; + } + + @Override + public boolean isFound(){ + return findings.size() == 1; + } + + @Override + public boolean wantLessEntropy(){ + return findings.size() > 1; + } + + @Override + public int getOffset() throws Exception{ + if(findings.isEmpty()) + throw new Exception("Nothing found"); + if (findings.size() > 1) + throw new Exception("Too many offsets"); + return findings.get(0); + } + + @Override + public boolean setOffsetsNearby(int offsetNearby) { + findings.removeIf(offset -> { + if (offset > offsetNearby) + return ! (offset < offsetNearby - 0xffff); + return ! (offset > offsetNearby - 0xffff); + }); + return isFound(); + } + + public String getDetails(Integer value){ + StringBuilder builder = new StringBuilder(); + int cbzOffsetInternal = value - 4; + int cbzExpression = Converter.getLEint(where, cbzOffsetInternal); + int conditionalJumpLocation = ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff; + + int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation); + int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation+4); + + builder.append(BinToAsmPrinter.printSimplified(cbzExpression, cbzOffsetInternal)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+4), cbzOffsetInternal+4)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+8), cbzOffsetInternal+8)); + builder.append("...\n"); + + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation)); + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation+4)); + + return builder.toString(); + } + + @Override + public String getDetails(){ + StringBuilder builder = new StringBuilder(); + int cbzOffsetInternal = findings.get(0) - 4; + int cbzExpression = Converter.getLEint(where, cbzOffsetInternal); + int conditionalJumpLocation = ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff; + + int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation); + int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation+4); + + builder.append(BinToAsmPrinter.printSimplified(cbzExpression, cbzOffsetInternal)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+4), cbzOffsetInternal+4)); + builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+8), cbzOffsetInternal+8)); + builder.append("...\n"); + + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation)); + builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation+4)); + + return builder.toString(); + } +} diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsWizard.java b/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsWizard.java new file mode 100644 index 0000000..3d3d240 --- /dev/null +++ b/src/main/java/nsusbloader/Utilities/patches/fs/finders/HeuristicFsWizard.java @@ -0,0 +1,139 @@ +/* + Copyright 2018-2022 Dmitry Isaenko + + This file is part of NS-USBloader. + + NS-USBloader 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. + + NS-USBloader 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 NS-USBloader. If not, see . + */ +package nsusbloader.Utilities.patches.fs.finders; + +import nsusbloader.Utilities.patches.AHeuristic; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class HeuristicFsWizard { + /* + private final List all; + private final List found; + private final List wantLessEntropy; + */ + private final boolean isFat; + private List all; + private List found; + private List wantLessEntropy; + + private final StringBuilder errorsAndNotes; + + private int offset1 = -1; + private int offset2 = -1; + + public HeuristicFsWizard(long fwVersion, byte[] where, boolean isFat) throws Exception{ + this.isFat = isFat; + this.errorsAndNotes = new StringBuilder(); + + if (isFat){ + this.all = Arrays.asList( + new HeuristicFsFAT1(fwVersion, where), + new HeuristicFsFAT2(fwVersion, where) + ); + } + else { + System.out.println("TODO: IMPLEMENT FOR EXFAT"); + return; + /* + this.all = Arrays.asList( + new HeuristicFsExFAT1(fwVersion, where), + new HeuristicFsExFAT2(fwVersion, where) + ); + */ + } +/* + this.found = all.stream() + .filter(AHeuristic::isFound) + .collect(Collectors.toList()); + + if (found.isEmpty()) + throw new Exception("Nothing found!"); + + this.wantLessEntropy = all.stream() + .filter(AHeuristic::wantLessEntropy) + .collect(Collectors.toList()); +/* FIXME + shareOffsetsWithEachOther(); + + assignOffset1(); + assignOffset2(); + */ + } + + private void shareOffsetsWithEachOther(){ + for (AHeuristic es : wantLessEntropy) { + if (shareWithNext(es)) + return; + } + } + private boolean shareWithNext(AHeuristic es){ + try { + for (AHeuristic foundEs : found) { + if (es.setOffsetsNearby(foundEs.getOffset())) { + found.add(es); + wantLessEntropy.remove(es); + shareOffsetsWithEachOther(); + return true; + } + } + } + catch (Exception e){ e.printStackTrace(); } + return false; + } + + private void assignOffset1(){ + try { + offset1 = all.get(0).getOffset(); + } + catch (Exception e){ errorsAndNotes.append(e.getLocalizedMessage()).append("\n"); } + } + private void assignOffset2(){ + try { + offset2 = all.get(1).getOffset(); + } + catch (Exception e){ errorsAndNotes.append(e.getLocalizedMessage()).append("\n"); } + } + + public String getErrorsAndNotes(){ + return errorsAndNotes.toString(); + } + + public String getDebug(){ + StringBuilder builder = new StringBuilder(); + if (isFat) + builder.append("\t\t--[ FAT32 ]--\n"); + else + builder.append("\t\t--[ ExFAT ]--\n"); + if (all.get(0).isFound()){ + builder.append("\t\t-=== 1 ===-\n"); + builder.append(all.get(0).getDetails()); + } + if (all.get(1).isFound()){ + builder.append("\t\t-=== 2 ===-\n"); + builder.append(all.get(1).getDetails()); + } + return builder.toString(); + } + + public int getOffset1() { return offset1; } + public int getOffset2() { return offset2; } +} diff --git a/src/main/resources/PatchesTab.fxml b/src/main/resources/PatchesTab.fxml index 801bf17..d888c2c 100644 --- a/src/main/resources/PatchesTab.fxml +++ b/src/main/resources/PatchesTab.fxml @@ -14,7 +14,7 @@ - + @@ -110,9 +110,10 @@ - +