This commit is contained in:
parent
4ccf833aa5
commit
f8b60af41b
21 changed files with 1447 additions and 86 deletions
|
@ -39,20 +39,21 @@ import nsusbloader.MediatorControl;
|
||||||
import nsusbloader.NSLDataTypes.EModule;
|
import nsusbloader.NSLDataTypes.EModule;
|
||||||
import nsusbloader.ServiceWindow;
|
import nsusbloader.ServiceWindow;
|
||||||
import nsusbloader.Utilities.patches.es.EsPatchMaker;
|
import nsusbloader.Utilities.patches.es.EsPatchMaker;
|
||||||
|
import nsusbloader.Utilities.patches.fs.FsPatchMaker;
|
||||||
|
|
||||||
// TODO: CLI SUPPORT
|
// TODO: CLI SUPPORT
|
||||||
public class PatchesController implements Initializable {
|
public class PatchesController implements Initializable {
|
||||||
@FXML
|
@FXML
|
||||||
private VBox patchesToolPane;
|
private VBox patchesToolPane;
|
||||||
@FXML
|
@FXML
|
||||||
private Button selFwFolderBtn, selProdKeysBtn, makeEsBtn;
|
private Button selFwFolderBtn, selProdKeysBtn, makeEsBtn, makeFsBtn;
|
||||||
@FXML
|
@FXML
|
||||||
private Label shortNameFirmwareLbl, locationFirmwareLbl, saveToLbl, shortNameKeysLbl, locationKeysLbl, statusLbl;
|
private Label shortNameFirmwareLbl, locationFirmwareLbl, saveToLbl, shortNameKeysLbl, locationKeysLbl, statusLbl;
|
||||||
private Thread workThread;
|
private Thread workThread;
|
||||||
|
|
||||||
private String previouslyOpenedPath;
|
private String previouslyOpenedPath;
|
||||||
private ResourceBundle resourceBundle;
|
private ResourceBundle resourceBundle;
|
||||||
private Region convertRegion;
|
private Region convertRegionEs;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||||
|
@ -70,9 +71,10 @@ public class PatchesController implements Initializable {
|
||||||
locationKeysLbl.textProperty().addListener((observableValue, currentText, updatedText) ->
|
locationKeysLbl.textProperty().addListener((observableValue, currentText, updatedText) ->
|
||||||
shortNameKeysLbl.setText(updatedText.replaceAll(myRegexp, "")));
|
shortNameKeysLbl.setText(updatedText.replaceAll(myRegexp, "")));
|
||||||
|
|
||||||
convertRegion = new Region();
|
convertRegionEs = new Region();
|
||||||
convertRegion.getStyleClass().add("regionCake");
|
convertRegionEs.getStyleClass().add("regionCake");
|
||||||
makeEsBtn.setGraphic(convertRegion);
|
makeEsBtn.setGraphic(convertRegionEs);
|
||||||
|
//makeFsBtn.setGraphic(convertRegionEs);
|
||||||
|
|
||||||
AppPreferences preferences = AppPreferences.getInstance();
|
AppPreferences preferences = AppPreferences.getInstance();
|
||||||
String keysLocation = preferences.getKeysLocation();
|
String keysLocation = preferences.getKeysLocation();
|
||||||
|
@ -85,6 +87,7 @@ public class PatchesController implements Initializable {
|
||||||
saveToLbl.setText(preferences.getPatchesSaveToLocation());
|
saveToLbl.setText(preferences.getPatchesSaveToLocation());
|
||||||
//makeEsBtn.disableProperty().bind(Bindings.isEmpty(locationFirmwareLbl.textProperty()));
|
//makeEsBtn.disableProperty().bind(Bindings.isEmpty(locationFirmwareLbl.textProperty()));
|
||||||
makeEsBtn.setOnAction(actionEvent -> makeEs());
|
makeEsBtn.setOnAction(actionEvent -> makeEs());
|
||||||
|
makeFsBtn.setOnAction(actionEvent -> makeFs());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,6 +177,30 @@ public class PatchesController implements Initializable {
|
||||||
workThread.setDaemon(true);
|
workThread.setDaemon(true);
|
||||||
workThread.start();
|
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(){
|
private void interruptProcessOfPatchMaking(){
|
||||||
if (workThread == null || ! workThread.isAlive())
|
if (workThread == null || ! workThread.isAlive())
|
||||||
return;
|
return;
|
||||||
|
@ -187,11 +214,11 @@ public class PatchesController implements Initializable {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
convertRegion.getStyleClass().clear();
|
convertRegionEs.getStyleClass().clear();
|
||||||
|
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
MediatorControl.getInstance().getContoller().logArea.clear();
|
MediatorControl.getInstance().getContoller().logArea.clear();
|
||||||
convertRegion.getStyleClass().add("regionStop");
|
convertRegionEs.getStyleClass().add("regionStop");
|
||||||
|
|
||||||
makeEsBtn.setOnAction(e-> interruptProcessOfPatchMaking());
|
makeEsBtn.setOnAction(e-> interruptProcessOfPatchMaking());
|
||||||
makeEsBtn.setText(resourceBundle.getString("btn_Stop"));
|
makeEsBtn.setText(resourceBundle.getString("btn_Stop"));
|
||||||
|
@ -199,7 +226,7 @@ public class PatchesController implements Initializable {
|
||||||
makeEsBtn.getStyleClass().add("buttonStop");
|
makeEsBtn.getStyleClass().add("buttonStop");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
convertRegion.getStyleClass().add("regionCake");
|
convertRegionEs.getStyleClass().add("regionCake");
|
||||||
|
|
||||||
makeEsBtn.setOnAction(actionEvent -> makeEs());
|
makeEsBtn.setOnAction(actionEvent -> makeEs());
|
||||||
makeEsBtn.setText(resourceBundle.getString("tabPatches_Btn_MakeEs"));
|
makeEsBtn.setText(resourceBundle.getString("tabPatches_Btn_MakeEs"));
|
||||||
|
|
45
src/main/java/nsusbloader/Utilities/patches/AHeuristic.java
Normal file
45
src/main/java/nsusbloader/Utilities/patches/AHeuristic.java
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
|
@ -16,10 +16,9 @@
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
along with NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package nsusbloader.Utilities.patches.es;
|
package nsusbloader.Utilities.patches;
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
import libKonogonka.Converter;
|
||||||
import nsusbloader.Main;
|
|
||||||
import nsusbloader.NSLMain;
|
import nsusbloader.NSLMain;
|
||||||
|
|
||||||
public class BinToAsmPrinter {
|
public class BinToAsmPrinter {
|
||||||
|
@ -123,6 +122,12 @@ public class BinToAsmPrinter {
|
||||||
}
|
}
|
||||||
case 0xA2:
|
case 0xA2:
|
||||||
return printSUBSimplified(instructionExpression, offset);
|
return printSUBSimplified(instructionExpression, offset);
|
||||||
|
case 0xE2:
|
||||||
|
case 0x1e2:
|
||||||
|
return printCMPSimplified(instructionExpression, offset);
|
||||||
|
case 0x24:
|
||||||
|
case 0x124:
|
||||||
|
return printANDSimplified(instructionExpression, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (instructionExpression >> 24 & 0xff) {
|
switch (instructionExpression >> 24 & 0xff) {
|
||||||
|
@ -145,6 +150,7 @@ public class BinToAsmPrinter {
|
||||||
case 0x25:
|
case 0x25:
|
||||||
return printBLSimplified(instructionExpression, offset);
|
return printBLSimplified(instructionExpression, offset);
|
||||||
}
|
}
|
||||||
|
System.out.printf("0x%x\n", (instructionExpression >> 23 & 0xff));
|
||||||
return printUnknownSimplified(instructionExpression, offset);
|
return printUnknownSimplified(instructionExpression, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +417,7 @@ public class BinToAsmPrinter {
|
||||||
int conditionalJumpLocation = ((instructionExpression >> 4 & 0b1111111111111111111) * 4 + offset) & 0xfffff;
|
int conditionalJumpLocation = ((instructionExpression >> 4 & 0b1111111111111111111) * 4 + offset) & 0xfffff;
|
||||||
|
|
||||||
return String.format(
|
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,
|
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
getBConditionalMarker(instructionExpression & 0xf),
|
getBConditionalMarker(instructionExpression & 0xf),
|
||||||
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
||||||
|
@ -437,8 +443,8 @@ public class BinToAsmPrinter {
|
||||||
|
|
||||||
private static String printMOVRegisterSimplified(int instructionExpression, int offset){ //ADD (immediate)
|
private static String printMOVRegisterSimplified(int instructionExpression, int offset){ //ADD (immediate)
|
||||||
String sfHw = (instructionExpression >> 31 & 1) == 0 ? "W" : "X";
|
String sfHw = (instructionExpression >> 31 & 1) == 0 ? "W" : "X";
|
||||||
int Rm = instructionExpression >> 16 & 0xF;
|
int Rm = instructionExpression >> 16 & 0x1F;
|
||||||
int Rd = instructionExpression & 0xF;
|
int Rd = instructionExpression & 0x1F;
|
||||||
|
|
||||||
return String.format(
|
return String.format(
|
||||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV (reg) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n",
|
"%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);
|
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){
|
private static String printUnknownSimplified(int instructionExpression, int offset){
|
||||||
return String.format(
|
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);
|
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
along with NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package nsusbloader.Utilities.patches.es;
|
package nsusbloader.Utilities.patches;
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
import libKonogonka.Converter;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package nsusbloader.Utilities.patches.es;
|
package nsusbloader.Utilities.patches.es;
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
import libKonogonka.Converter;
|
||||||
import libKonogonka.Tools.NCA.NCAProvider;
|
import libKonogonka.fs.NCA.NCAProvider;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
|
@ -22,11 +22,12 @@
|
||||||
package nsusbloader.Utilities.patches.es;
|
package nsusbloader.Utilities.patches.es;
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
import libKonogonka.Converter;
|
||||||
import libKonogonka.Tools.NCA.NCAProvider;
|
import libKonogonka.fs.NCA.NCAProvider;
|
||||||
import libKonogonka.Tools.NSO.NSO0Header;
|
import libKonogonka.fs.NSO.NSO0Header;
|
||||||
import libKonogonka.Tools.NSO.NSO0Provider;
|
import libKonogonka.fs.NSO.NSO0Provider;
|
||||||
import nsusbloader.ModelControllers.ILogPrinter;
|
import nsusbloader.ModelControllers.ILogPrinter;
|
||||||
import nsusbloader.NSLDataTypes.EMsgType;
|
import nsusbloader.NSLDataTypes.EMsgType;
|
||||||
|
import nsusbloader.Utilities.patches.BinToAsmPrinter;
|
||||||
import nsusbloader.Utilities.patches.es.finders.HeuristicEsWizard;
|
import nsusbloader.Utilities.patches.es.finders.HeuristicEsWizard;
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
|
@ -35,6 +36,7 @@ import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class EsPatch {
|
public class EsPatch {
|
||||||
|
@ -113,7 +115,7 @@ public class EsPatch {
|
||||||
handyEsPatch.put(getFooter());
|
handyEsPatch.put(getFooter());
|
||||||
|
|
||||||
try (BufferedOutputStream stream = new BufferedOutputStream(
|
try (BufferedOutputStream stream = new BufferedOutputStream(
|
||||||
Files.newOutputStream(new File(patchFileLocation).toPath()))){
|
Files.newOutputStream(Paths.get(patchFileLocation)))){
|
||||||
stream.write(handyEsPatch.array());
|
stream.write(handyEsPatch.array());
|
||||||
}
|
}
|
||||||
logPrinter.print("Patch created at "+patchFileLocation, EMsgType.PASS);
|
logPrinter.print("Patch created at "+patchFileLocation, EMsgType.PASS);
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package nsusbloader.Utilities.patches.es;
|
package nsusbloader.Utilities.patches.es;
|
||||||
|
|
||||||
import libKonogonka.KeyChainHolder;
|
import libKonogonka.KeyChainHolder;
|
||||||
import libKonogonka.Tools.NCA.NCAProvider;
|
import libKonogonka.fs.NCA.NCAProvider;
|
||||||
import nsusbloader.ModelControllers.CancellableRunnable;
|
import nsusbloader.ModelControllers.CancellableRunnable;
|
||||||
import nsusbloader.ModelControllers.ILogPrinter;
|
import nsusbloader.ModelControllers.ILogPrinter;
|
||||||
import nsusbloader.ModelControllers.Log;
|
import nsusbloader.ModelControllers.Log;
|
||||||
|
@ -92,6 +92,8 @@ public class EsPatchMaker extends CancellableRunnable {
|
||||||
private void receiveFirmware() throws Exception{
|
private void receiveFirmware() throws Exception{
|
||||||
logPrinter.print("Looking at firmware", EMsgType.INFO);
|
logPrinter.print("Looking at firmware", EMsgType.INFO);
|
||||||
this.firmware = new File(pathToFirmware);
|
this.firmware = new File(pathToFirmware);
|
||||||
|
if (! firmware.exists())
|
||||||
|
throw new Exception("Firmware directory does not exist " + pathToFirmware);
|
||||||
}
|
}
|
||||||
private void buildKeyChainHolder() throws Exception{
|
private void buildKeyChainHolder() throws Exception{
|
||||||
logPrinter.print("Reading keys", EMsgType.INFO);
|
logPrinter.print("Reading keys", EMsgType.INFO);
|
||||||
|
@ -174,7 +176,7 @@ public class EsPatchMaker extends CancellableRunnable {
|
||||||
subTasks.add(task);
|
subTasks.add(task);
|
||||||
return subTasks;
|
return subTasks;
|
||||||
}
|
}
|
||||||
private List<NCAProvider> getNextSet(Iterator<String> iterator, int amount) throws Exception{
|
private List<NCAProvider> getNextSet(Iterator<String> iterator, int amount) throws Exception{
|
||||||
List<NCAProvider> ncas = new ArrayList<>();
|
List<NCAProvider> ncas = new ArrayList<>();
|
||||||
for (int j = 0; j < amount; j++){
|
for (int j = 0; j < amount; j++){
|
||||||
String ncaFileName = iterator.next();
|
String ncaFileName = iterator.next();
|
||||||
|
|
|
@ -19,12 +19,13 @@
|
||||||
package nsusbloader.Utilities.patches.es.finders;
|
package nsusbloader.Utilities.patches.es.finders;
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
import libKonogonka.Converter;
|
||||||
import nsusbloader.Utilities.patches.es.BinToAsmPrinter;
|
import nsusbloader.Utilities.patches.AHeuristic;
|
||||||
import nsusbloader.Utilities.patches.es.SimplyFind;
|
import nsusbloader.Utilities.patches.BinToAsmPrinter;
|
||||||
|
import nsusbloader.Utilities.patches.SimplyFind;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class HeuristicEs1 implements IHeuristicEs {
|
class HeuristicEs1 extends AHeuristic {
|
||||||
private static final String PATTERN = "1F90013128.8052";
|
private static final String PATTERN = "1F90013128.8052";
|
||||||
|
|
||||||
private final List<Integer> findings;
|
private final List<Integer> findings;
|
||||||
|
|
|
@ -19,13 +19,14 @@
|
||||||
package nsusbloader.Utilities.patches.es.finders;
|
package nsusbloader.Utilities.patches.es.finders;
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
import libKonogonka.Converter;
|
||||||
import nsusbloader.Utilities.patches.es.BinToAsmPrinter;
|
import nsusbloader.Utilities.patches.AHeuristic;
|
||||||
import nsusbloader.Utilities.patches.es.SimplyFind;
|
import nsusbloader.Utilities.patches.BinToAsmPrinter;
|
||||||
|
import nsusbloader.Utilities.patches.SimplyFind;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class HeuristicEs2 implements IHeuristicEs {
|
class HeuristicEs2 extends AHeuristic {
|
||||||
private static final String PATTERN = ".D2.52";
|
private static final String PATTERN = ".D2.52";
|
||||||
|
|
||||||
private List<Integer> findings;
|
private List<Integer> findings;
|
||||||
|
|
|
@ -19,12 +19,13 @@
|
||||||
package nsusbloader.Utilities.patches.es.finders;
|
package nsusbloader.Utilities.patches.es.finders;
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
import libKonogonka.Converter;
|
||||||
import nsusbloader.Utilities.patches.es.BinToAsmPrinter;
|
import nsusbloader.Utilities.patches.AHeuristic;
|
||||||
import nsusbloader.Utilities.patches.es.SimplyFind;
|
import nsusbloader.Utilities.patches.BinToAsmPrinter;
|
||||||
|
import nsusbloader.Utilities.patches.SimplyFind;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class HeuristicEs3 implements IHeuristicEs {
|
class HeuristicEs3 extends AHeuristic {
|
||||||
private static final String PATTERN0 = "..FF97";
|
private static final String PATTERN0 = "..FF97";
|
||||||
private static final String PATTERN1 = "......FF97"; // aka "E0230091..FF97";
|
private static final String PATTERN1 = "......FF97"; // aka "E0230091..FF97";
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,16 @@
|
||||||
*/
|
*/
|
||||||
package nsusbloader.Utilities.patches.es.finders;
|
package nsusbloader.Utilities.patches.es.finders;
|
||||||
|
|
||||||
|
import nsusbloader.Utilities.patches.AHeuristic;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class HeuristicEsWizard {
|
public class HeuristicEsWizard {
|
||||||
private final List<IHeuristicEs> all;
|
private final List<AHeuristic> all;
|
||||||
private final List<IHeuristicEs> found;
|
private final List<AHeuristic> found;
|
||||||
private final List<IHeuristicEs> wantLessEntropy;
|
private final List<AHeuristic> wantLessEntropy;
|
||||||
|
|
||||||
private final StringBuilder errorsAndNotes;
|
private final StringBuilder errorsAndNotes;
|
||||||
|
|
||||||
|
@ -43,14 +45,14 @@ public class HeuristicEsWizard {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.found = all.stream()
|
this.found = all.stream()
|
||||||
.filter(IHeuristicEs::isFound)
|
.filter(AHeuristic::isFound)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (found.isEmpty())
|
if (found.isEmpty())
|
||||||
throw new Exception("Nothing found!");
|
throw new Exception("Nothing found!");
|
||||||
|
|
||||||
this.wantLessEntropy = all.stream()
|
this.wantLessEntropy = all.stream()
|
||||||
.filter(IHeuristicEs::wantLessEntropy)
|
.filter(AHeuristic::wantLessEntropy)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
shareOffsetsWithEachOther();
|
shareOffsetsWithEachOther();
|
||||||
|
@ -61,14 +63,14 @@ public class HeuristicEsWizard {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shareOffsetsWithEachOther(){
|
private void shareOffsetsWithEachOther(){
|
||||||
for (IHeuristicEs es : wantLessEntropy) {
|
for (AHeuristic es : wantLessEntropy) {
|
||||||
if (shareWithNext(es))
|
if (shareWithNext(es))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private boolean shareWithNext(IHeuristicEs es){
|
private boolean shareWithNext(AHeuristic es){
|
||||||
try {
|
try {
|
||||||
for (IHeuristicEs foundEs : found) {
|
for (AHeuristic foundEs : found) {
|
||||||
if (es.setOffsetsNearby(foundEs.getOffset())) {
|
if (es.setOffsetsNearby(foundEs.getOffset())) {
|
||||||
found.add(es);
|
found.add(es);
|
||||||
wantLessEntropy.remove(es);
|
wantLessEntropy.remove(es);
|
||||||
|
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<NCAProvider> {
|
||||||
|
private final List<NCAProvider> ncaProviders;
|
||||||
|
|
||||||
|
FsNcaSearchTask(List<NCAProvider> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
208
src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java
Normal file
208
src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
---
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
192
src/main/java/nsusbloader/Utilities/patches/fs/FsPatchMaker.java
Normal file
192
src/main/java/nsusbloader/Utilities/patches/fs/FsPatchMaker.java
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<String> 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<String, File> 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<Future<NCAProvider>> futuresResults = executorService.invokeAll(getSubTasksCollection());
|
||||||
|
int counter = 0;
|
||||||
|
for (Future<NCAProvider> 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<Callable<NCAProvider>> getSubTasksCollection() throws Exception{
|
||||||
|
logPrinter.print("Forming sub-tasks collection", EMsgType.INFO);
|
||||||
|
List<Callable<NCAProvider>> subTasks = new ArrayList<>();
|
||||||
|
|
||||||
|
int ncaPerThreadAmount = ncaFilesList.size() / THREADS_POOL_SIZE;
|
||||||
|
Iterator<String> iterator = ncaFilesList.listIterator();
|
||||||
|
|
||||||
|
for (int i = 1; i < THREADS_POOL_SIZE; i++){
|
||||||
|
Callable<NCAProvider> task = new FsNcaSearchTask(getNextSet(iterator, ncaPerThreadAmount));
|
||||||
|
subTasks.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
Callable<NCAProvider> task = new FsNcaSearchTask(getNextSet(iterator,
|
||||||
|
ncaFilesList.size() % THREADS_POOL_SIZE == 0 ? ncaPerThreadAmount : ncaPerThreadAmount+1));
|
||||||
|
subTasks.add(task);
|
||||||
|
return subTasks;
|
||||||
|
}
|
||||||
|
private List<NCAProvider> getNextSet(Iterator<String> iterator, int amount) throws Exception{
|
||||||
|
List<NCAProvider> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<Integer> 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<Integer> 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<Integer> findings;
|
||||||
|
private final byte[] where;
|
||||||
|
|
||||||
|
HeuristicFsFAT1(long fwVersion, byte[] where){
|
||||||
|
this.where = where;
|
||||||
|
String pattern = getPattern(fwVersion);
|
||||||
|
SimplyFind simplyfind = new SimplyFind(pattern, where);
|
||||||
|
List<Integer> 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<Integer> 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<AHeuristic> all;
|
||||||
|
private final List<AHeuristic> found;
|
||||||
|
private final List<AHeuristic> wantLessEntropy;
|
||||||
|
*/
|
||||||
|
private final boolean isFat;
|
||||||
|
private List<AHeuristic> all;
|
||||||
|
private List<AHeuristic> found;
|
||||||
|
private List<AHeuristic> 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; }
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
<?import javafx.scene.shape.SVGPath?>
|
<?import javafx.scene.shape.SVGPath?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<ScrollPane fitToWidth="true" onDragDropped="#handleDrop" onDragOver="#handleDragOver" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nsusbloader.Controllers.PatchesController">
|
<ScrollPane fitToWidth="true" onDragDropped="#handleDrop" onDragOver="#handleDragOver" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nsusbloader.Controllers.PatchesController">
|
||||||
<VBox fx:id="patchesToolPane" spacing="15.0">
|
<VBox fx:id="patchesToolPane" spacing="15.0">
|
||||||
<Pane minHeight="-Infinity" prefHeight="10.0" style="-fx-background-color: linear-gradient(from 41px 34px to 50px 50px, reflect, #2cd882 40%, transparent 45%);" />
|
<Pane minHeight="-Infinity" prefHeight="10.0" style="-fx-background-color: linear-gradient(from 41px 34px to 50px 50px, reflect, #2cd882 40%, transparent 45%);" />
|
||||||
<HBox alignment="CENTER">
|
<HBox alignment="CENTER">
|
||||||
|
@ -110,9 +110,10 @@
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<Pane VBox.vgrow="ALWAYS" />
|
<Pane VBox.vgrow="ALWAYS" />
|
||||||
<HBox alignment="CENTER">
|
<HBox alignment="CENTER" spacing="5.0">
|
||||||
<children>
|
<children>
|
||||||
<Button fx:id="makeEsBtn" contentDisplay="TOP" mnemonicParsing="false" styleClass="buttonUp" text="%tabPatches_Btn_MakeEs" />
|
<Button fx:id="makeEsBtn" contentDisplay="TOP" mnemonicParsing="false" styleClass="buttonUp" text="%tabPatches_Btn_MakeEs" />
|
||||||
|
<Button fx:id="makeFsBtn" contentDisplay="TOP" mnemonicParsing="false" styleClass="buttonUp" text="%tabPatches_Btn_MakeFs" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<padding>
|
<padding>
|
||||||
|
|
Loading…
Reference in a new issue