Correct readme, update few icons, add freedesktop entry and svg icon. Could be useful for packagers.

This commit is contained in:
Dmitry Isaenko 2022-12-23 07:08:22 +03:00
parent 8b23b8967b
commit 55df39923f
32 changed files with 2433 additions and 205 deletions

View file

@ -88,6 +88,8 @@ public class AppPreferences {
public String getHostIp(){ return preferences.get("HOSTIP", "0.0.0.0").replaceAll("(\\s)|(\t)", "");} // who the hell said 'paranoid'?
public void setHostIp(String ip){preferences.put("HOSTIP", ip);}
public void give(){preferences.putBoolean("man", true);}
public boolean take(){return preferences.getBoolean("man", false);}
public String getHostPort(){
String value = preferences.get("HOSTPORT", "6042");
if (!value.matches("^[0-9]{1,5}$"))
@ -138,4 +140,10 @@ public class AppPreferences {
public String getLastOpenedTab(){ return preferences.get("recent_tab", ""); }
public void setLastOpenedTab(String tabId){ preferences.put("recent_tab", tabId); }
// Patches
public String getKeysLocation(){ return preferences.get("keys", ""); }
public void setKeysLocation(String path){ preferences.put("keys", path); }
public String getPatchesSaveToLocation(){ return FilesHelper.getRealFolder(preferences.get("patches_saveto", System.getProperty("user.home"))); }
public void setPatchesSaveToLocation(String value){ preferences.put("patches_saveto", value); }
}

View file

@ -43,7 +43,7 @@ public class NSLMainController implements Initializable {
@FXML
private TabPane mainTabPane;
@FXML
private Tab GamesTabHolder, RCMTabHolder, SMTabHolder;
private Tab GamesTabHolder, RCMTabHolder, SMTabHolder, PatchesTabHolder;
@FXML
private GamesController GamesTabController;
@ -55,6 +55,8 @@ public class NSLMainController implements Initializable {
private RcmController RcmTabController;
@FXML
private NxdtController NXDTabController;
@FXML
private PatchesController PatchesTabController;
@Override
public void initialize(URL url, ResourceBundle rb) {
@ -71,7 +73,7 @@ public class NSLMainController implements Initializable {
if (AppPreferences.getInstance().getAutoCheckUpdates()){
checkForUpdates();
}
if (! AppPreferences.getInstance().take()) mainTabPane.getTabs().remove(3);
openLastOpenedTab();
}
private void checkForUpdates(){
@ -127,6 +129,8 @@ public class NSLMainController implements Initializable {
public RcmController getRcmCtrlr(){ return RcmTabController; }
public NxdtController getNXDTabController(){ return NXDTabController; }
public PatchesController getPatchesTabController(){ return PatchesTabController; }
/**
* Save preferences before exit
* */
@ -136,7 +140,7 @@ public class NSLMainController implements Initializable {
SplitMergeTabController.updatePreferencesOnExit(); // NOTE: This shit above should be re-written to similar pattern
RcmTabController.updatePreferencesOnExit();
NXDTabController.updatePreferencesOnExit();
PatchesTabController.updatePreferencesOnExit();
saveLastOpenedTab();
}
@ -152,6 +156,9 @@ public class NSLMainController implements Initializable {
case "SMTabHolder":
mainTabPane.getSelectionModel().select(SMTabHolder);
break;
case "PatchesTabHolder":
mainTabPane.getSelectionModel().select(PatchesTabHolder);
break;
}
}
private void saveLastOpenedTab(){

View file

@ -0,0 +1,225 @@
/*
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.Controllers;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.input.DragEvent;
import javafx.scene.input.TransferMode;
import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import nsusbloader.AppPreferences;
import nsusbloader.FilesHelper;
import nsusbloader.MediatorControl;
import nsusbloader.NSLDataTypes.EModule;
import nsusbloader.ServiceWindow;
import nsusbloader.Utilities.patches.es.EsPatchMaker;
// TODO: CLI SUPPORT
public class PatchesController implements Initializable {
@FXML
private VBox patchesToolPane;
@FXML
private Button selFwFolderBtn, selProdKeysBtn, makeEsBtn;
@FXML
private Label shortNameFirmwareLbl, locationFirmwareLbl, saveToLbl, shortNameKeysLbl, locationKeysLbl, statusLbl;
private Thread workThread;
private String previouslyOpenedPath;
private ResourceBundle resourceBundle;
private Region convertRegion;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
this.resourceBundle = resourceBundle;
this.previouslyOpenedPath = System.getProperty("user.home");
String myRegexp;
if (File.separator.equals("/"))
myRegexp = "^.+/";
else
myRegexp = "^.+\\\\";
locationFirmwareLbl.textProperty().addListener((observableValue, currentText, updatedText) ->
shortNameFirmwareLbl.setText(updatedText.replaceAll(myRegexp, "")));
locationKeysLbl.textProperty().addListener((observableValue, currentText, updatedText) ->
shortNameKeysLbl.setText(updatedText.replaceAll(myRegexp, "")));
convertRegion = new Region();
convertRegion.getStyleClass().add("regionCake");
makeEsBtn.setGraphic(convertRegion);
AppPreferences preferences = AppPreferences.getInstance();
String keysLocation = preferences.getKeysLocation();
File keysFile = new File(keysLocation);
if (keysFile.exists() && keysFile.isFile()) {
locationKeysLbl.setText(keysLocation);
}
saveToLbl.setText(preferences.getPatchesSaveToLocation());
//makeEsBtn.disableProperty().bind(Bindings.isEmpty(locationFirmwareLbl.textProperty()));
makeEsBtn.setOnAction(actionEvent -> makeEs());
}
/**
* Drag-n-drop support (dragOver consumer)
* */
@FXML
private void handleDragOver(DragEvent event){
if (event.getDragboard().hasFiles())
event.acceptTransferModes(TransferMode.ANY);
event.consume();
}
/**
* Drag-n-drop support (drop consumer)
* */
@FXML
private void handleDrop(DragEvent event){
List<File> filesDropped = event.getDragboard().getFiles();
for (File file : filesDropped){
if (file.isDirectory()) {
locationFirmwareLbl.setText(file.getAbsolutePath());
continue;
}
String fileName = file.getName().toLowerCase();
if ((fileName.endsWith(".dat")) ||
(fileName.endsWith(".keys") &&
! fileName.equals("dev.keys") &&
! fileName.equals("title.keys")))
locationKeysLbl.setText(file.getAbsolutePath());
}
event.setDropCompleted(true);
event.consume();
}
@FXML
private void selectFirmware(){
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle(resourceBundle.getString("tabPatches_Lbl_Firmware"));
directoryChooser.setInitialDirectory(new File(FilesHelper.getRealFolder(previouslyOpenedPath)));
File firmware = directoryChooser.showDialog(patchesToolPane.getScene().getWindow());
if (firmware == null)
return;
locationFirmwareLbl.setText(firmware.getAbsolutePath());
}
@FXML
private void selectSaveTo(){
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle(resourceBundle.getString("tabSplMrg_Btn_SelectFolder"));
directoryChooser.setInitialDirectory(new File(FilesHelper.getRealFolder(previouslyOpenedPath)));
File saveToDir = directoryChooser.showDialog(patchesToolPane.getScene().getWindow());
if (saveToDir == null)
return;
saveToLbl.setText(saveToDir.getAbsolutePath());
}
@FXML
private void selectProdKeys(){
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle(resourceBundle.getString("tabPatches_Lbl_Keys"));
fileChooser.setInitialDirectory(new File(FilesHelper.getRealFolder(previouslyOpenedPath)));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("keys", "*.dat", "*.keys"));
File keys = fileChooser.showOpenDialog(patchesToolPane.getScene().getWindow());
if (keys != null && keys.exists()) {
locationKeysLbl.setText(keys.getAbsolutePath());
}
}
private void makeEs(){
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;
}
EsPatchMaker esPatchMaker = new EsPatchMaker(locationFirmwareLbl.getText(), locationKeysLbl.getText(),
saveToLbl.getText());
workThread = new Thread(esPatchMaker);
workThread.setDaemon(true);
workThread.start();
}
private void interruptProcessOfPatchMaking(){
if (workThread == null || ! workThread.isAlive())
return;
workThread.interrupt();
}
public void notifyThreadStarted(boolean isActive, EModule type) {
if (! type.equals(EModule.PATCHES)) {
patchesToolPane.setDisable(isActive);
return;
}
convertRegion.getStyleClass().clear();
if (isActive) {
MediatorControl.getInstance().getContoller().logArea.clear();
convertRegion.getStyleClass().add("regionStop");
makeEsBtn.setOnAction(e-> interruptProcessOfPatchMaking());
makeEsBtn.setText(resourceBundle.getString("btn_Stop"));
makeEsBtn.getStyleClass().remove("buttonUp");
makeEsBtn.getStyleClass().add("buttonStop");
}
else {
convertRegion.getStyleClass().add("regionCake");
makeEsBtn.setOnAction(actionEvent -> makeEs());
makeEsBtn.setText(resourceBundle.getString("tabPatches_Btn_MakeEs"));
makeEsBtn.getStyleClass().remove("buttonStop");
makeEsBtn.getStyleClass().add("buttonUp");
}
}
public void setOneLineStatus(boolean statusSuccess){
if (statusSuccess)
statusLbl.setText(resourceBundle.getString("done_txt"));
else
statusLbl.setText(resourceBundle.getString("failure_txt"));
}
void updatePreferencesOnExit(){
AppPreferences.getInstance().setPatchesSaveToLocation(saveToLbl.getText());
if (locationKeysLbl.getText().isEmpty())
return;
AppPreferences.getInstance().setKeysLocation(locationKeysLbl.getText());
}
}

View file

@ -40,11 +40,12 @@ public class MediatorControl {
}
public NSLMainController getContoller(){ return mainController; }
public GamesController getGamesController(){ return mainController.getGamesCtrlr(); };
public SettingsController getSettingsController(){ return mainController.getSettingsCtrlr(); };
public SplitMergeController getSplitMergeController(){ return mainController.getSmCtrlr(); };
public RcmController getRcmController(){ return mainController.getRcmCtrlr(); };
public NxdtController getNxdtController(){ return mainController.getNXDTabController(); };
public GamesController getGamesController(){ return mainController.getGamesCtrlr(); }
public SettingsController getSettingsController(){ return mainController.getSettingsCtrlr(); }
public SplitMergeController getSplitMergeController(){ return mainController.getSmCtrlr(); }
public RcmController getRcmController(){ return mainController.getRcmCtrlr(); }
public NxdtController getNxdtController(){ return mainController.getNXDTabController(); }
public PatchesController getPatchesController(){ return mainController.getPatchesTabController(); }
public ResourceBundle getResourceBundle(){
return mainController.getResourceBundle();
@ -56,6 +57,7 @@ public class MediatorControl {
getSplitMergeController().notifyThreadStarted(isActive, appModuleType);
getRcmController().notifyThreadStarted(isActive, appModuleType);
getNxdtController().notifyThreadStarted(isActive, appModuleType);
getPatchesController().notifyThreadStarted(isActive, appModuleType);
}
public synchronized boolean getTransferActive() { return this.isTransferActive.get(); }
}

View file

@ -112,6 +112,8 @@ public class MessagesConsumer extends AnimationTimer {
case SPLIT_MERGE_TOOL:
MediatorControl.getInstance().getSplitMergeController().setOneLineStatus(oneLinerStatus.get());
break;
case PATCHES:
MediatorControl.getInstance().getPatchesController().setOneLineStatus(oneLinerStatus.get());
}
this.stop();
}

View file

@ -22,5 +22,6 @@ public enum EModule {
USB_NET_TRANSFERS,
SPLIT_MERGE_TOOL,
RCM,
NXDT
NXDT,
PATCHES
}

View file

@ -28,6 +28,7 @@ import nsusbloader.Controllers.NSLMainController;
import nsusbloader.cli.CommandLineInterface;
import java.util.Locale;
import java.util.Objects;
import java.util.ResourceBundle;
public class NSLMain extends Application {
@ -47,10 +48,10 @@ public class NSLMain extends Application {
Parent root = loader.load();
primaryStage.getIcons().addAll(
new Image(getClass().getResourceAsStream("/res/app_icon32x32.png")),
new Image(getClass().getResourceAsStream("/res/app_icon48x48.png")),
new Image(getClass().getResourceAsStream("/res/app_icon64x64.png")),
new Image(getClass().getResourceAsStream("/res/app_icon128x128.png"))
new Image(Objects.requireNonNull(getClass().getResourceAsStream("/res/app_icon32x32.png"))),
new Image(Objects.requireNonNull(getClass().getResourceAsStream("/res/app_icon48x48.png"))),
new Image(Objects.requireNonNull(getClass().getResourceAsStream("/res/app_icon64x64.png"))),
new Image(Objects.requireNonNull(getClass().getResourceAsStream("/res/app_icon128x128.png")))
);
primaryStage.setTitle("NS-USBloader "+appVersion);

View file

@ -0,0 +1,526 @@
/*
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;
import libKonogonka.Converter;
import nsusbloader.Main;
import nsusbloader.NSLMain;
public class BinToAsmPrinter {
static {
boolean notWindows = ! System.getProperty("os.name").toLowerCase().contains("windows");
if(notWindows && NSLMain.isCli){
ANSI_RESET = "\u001B[0m";
ANSI_GREEN = "\u001B[32m";
ANSI_BLUE = "\u001B[34m";
ANSI_YELLOW = "\u001B[33m";
ANSI_PURPLE = "\u001B[35m";
ANSI_CYAN = "\u001B[36m";
ANSI_RED = "\u001B[31m";
}
else {
ANSI_RESET = ANSI_RED = ANSI_GREEN = ANSI_BLUE = ANSI_YELLOW = ANSI_PURPLE = ANSI_CYAN = "";
}
}
private static final String ANSI_RESET;
private static final String ANSI_RED;
private static final String ANSI_GREEN;
private static final String ANSI_BLUE;
private static final String ANSI_YELLOW;
private static final String ANSI_PURPLE;
private static final String ANSI_CYAN;
public static String print(int instructionExpression, int offset){
if (instructionExpression == 0xd503201f)
return printNOP(instructionExpression);
if ((instructionExpression & 0x7FE0FFE0) == 0x2A0003E0) {
return printMOVRegister(instructionExpression);
}
switch ((instructionExpression >> 23 & 0b011111111)){
case 0xA5:
return printMOV(instructionExpression);
case 0x62:
if (((instructionExpression & 0x1f) == 0x1f)){
return printCMN(instructionExpression, offset);
}
}
switch (instructionExpression >> 24 & 0xff) {
case 0x34:
case 0xb4:
return printCBZ(instructionExpression, offset);
case 0xb5:
case 0x35:
return printCBNZ(instructionExpression, offset);
case 0x36:
case 0xb6:
return printTBZ(instructionExpression, offset);
case 0x54:
return printBConditional(instructionExpression, offset);
}
switch ((instructionExpression >> 26 & 0b111111)) {
case 0x5:
return printB(instructionExpression, offset);
case 0x25:
return printBL(instructionExpression, offset);
}
return printUnknown(instructionExpression);
}
public static String printSimplified(int instructionExpression, int offset){
if (instructionExpression == 0xd503201f)
return printNOPSimplified(instructionExpression, offset);
if ((instructionExpression & 0x7FE0FFE0) == 0x2A0003E0) {
return printMOVRegisterSimplified(instructionExpression, offset);
}
switch (instructionExpression >> 22 & 0b1011111111) {
case 0x2e5:
return printLRDImmUnsignSimplified(instructionExpression, offset);
case 0xe5:
return printLRDBImmUnsignSimplified(instructionExpression, offset);
}
if ((instructionExpression >> 21 & 0x7FF) == 0x1C2)
return printLDURBSimplified(instructionExpression, offset);
// same to (afterJumpExpression >> 23 & 0x1F9) != 0xA1
switch (instructionExpression >> 22 & 0x1FF){
case 0xA3: // 0b10100011
case 0xA7: // 0b10100111
case 0xA5: // 0b10100101
return printLDPSimplified(instructionExpression, offset);
}
switch ((instructionExpression >> 23 & 0xff)){
case 0xA5:
return printMOVSimplified(instructionExpression, offset);
case 0x22:
return printADDSimplified(instructionExpression, offset);
case 0x62:
if (((instructionExpression & 0x1f) == 0x1f)){
return printCMNSimplified(instructionExpression, offset);
}
case 0xA2:
return printSUBSimplified(instructionExpression, offset);
}
switch (instructionExpression >> 24 & 0xff) {
case 0x34:
case 0xb4:
return printCBZSimplified(instructionExpression, offset);
case 0xb5:
case 0x35:
return printCBNZSimplified(instructionExpression, offset);
case 0x36:
case 0xb6:
return printTBZSimplified(instructionExpression, offset);
case 0x54:
return printBConditionalSimplified(instructionExpression, offset);
}
switch ((instructionExpression >> 26 & 0b111111)) {
case 0x5:
return printBSimplified(instructionExpression, offset);
case 0x25:
return printBLSimplified(instructionExpression, offset);
}
return printUnknownSimplified(instructionExpression, offset);
}
private static String printCBZ(int instructionExpression, int offset){
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
return String.format(ANSI_YELLOW + "sf == 0 ? <Wt> else <Xt>\n" +
"CBZ <?t>, <label> |.....CBZ signature......|\n" +
ANSI_CYAN + " sf 0 1 1 0 1 0 0 |imm19..........................................................||Rd.............|" + ANSI_RESET + "\n" +
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
"Instruction (BE) : %s | %s\n\n"+
ANSI_YELLOW + "CBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
private static String printCBNZ(int instructionExpression, int offset){
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
return String.format(ANSI_YELLOW + "sf == 0 ? <Wt> else <Xt>\n" +
"CBNZ <?t>, <label> |.....CBZ signature......|\n" +
ANSI_CYAN + " sf 0 1 1 0 1 0 |imm19..........................................................||Rd.............|" + ANSI_RESET + "\n" +
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
"Instruction (BE) : %s | %s\n\n"+
ANSI_YELLOW + "CBNZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
private static String printCMN(int instructionExpression, int offset){
int Rn = instructionExpression >> 5 & 0x1F;
int imm = instructionExpression >> 10 & 0xFFF;
return String.format(ANSI_YELLOW + "sf == 0 ? <Wt> else <Xt>\n" +
"CMN <?n>, <label> |.....CMN signature...........| |..CMN signature.|\n" +
ANSI_CYAN+" sf 0 1 1 0 0 0 1 0 |imm12......................................||Rn.............| 1 1 1 1 1" + ANSI_RESET + "\n" +
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
"Instruction (BE) : %s | %s\n\n" +
ANSI_YELLOW + "CMN " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
(instructionExpression >> 31 == 0) ? "w" : "x", Rn, imm);
}
private static String printB(int instructionExpression, int offset){
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
return String.format(ANSI_YELLOW+"B <label> |....B signature...|\n" +
" "+ANSI_CYAN+" 0 0 0 1 0 1 |imm26...................................................................................|" + ANSI_RESET + "\n" +
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
"Instruction (BE) : %s | %s\n" +
ANSI_YELLOW + "%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
((instructionExpression >> 26 & 0b111111) == 5)?"B":"Some weird stuff",
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
}
private static String printBL(int instructionExpression, int offset){
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
return String.format(ANSI_YELLOW+"BL <label> |...BL signature...|\n" +
" "+ANSI_CYAN+" 1 0 0 1 0 1 |imm26...................................................................................|" + ANSI_RESET + "\n" +
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
"Instruction (BE) : %s | %s\n" +
ANSI_YELLOW + "%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
((instructionExpression >> 26 & 0b111111) == 25)?"BL":"Some weird stuff",
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
}
private static String printMOV(int instructionExpression){
int imm16 = instructionExpression >> 5 & 0xFFFF;
int sfHw = (instructionExpression >> 22 & 1);
return String.format(ANSI_YELLOW + "sf == 0 && hw == 0x ? <Wt> else <Xt>\n" +
"MOV <?t>, <label> |.....MOV signature...........|\n" +
ANSI_CYAN +" sf 1 0 1 0 0 1 0 1 |hw...|imm16.................................................||Rd.............|" + ANSI_RESET + "\n" +
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
"Instruction (BE) : %s | %s\n" +
ANSI_YELLOW + "MOV " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
(sfHw == 0) ? "w" : "x", (instructionExpression & 0b11111), imm16);
}
private static String printMOVRegister(int instructionExpression){
String sfHw = (instructionExpression >> 31 & 1) == 0 ? "W" : "X";
int Rm = instructionExpression >> 16 & 0xF;
int Rd = instructionExpression & 0xF;
return String.format(ANSI_YELLOW + "sf == 0 && hw == 0x ? <Wt> else <Xt>\n" +
"MOV (register) <?d>, <?m> |.....MOV (register) signature.......|\n" +
ANSI_CYAN +" sf 0 1 0 1 0 1 0 0 0 0 |Rm..............| 0 0 0 0 0 0 1 1 1 1 1 |Rd.............|" + ANSI_RESET + "\n" +
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
"Instruction (BE) : %s | %s\n" +
ANSI_YELLOW + "MOV(reg) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n",
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
sfHw, Rm, sfHw, Rd);
}
private static String printNOP(int instructionExpression){
return String.format(
ANSI_YELLOW+"NOP |.....NOP signature..........................................................................................|\n" +
ANSI_CYAN +" 1 1 0 1 0 1 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 1 1 1 1 " + ANSI_RESET + "\n" +
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n"+
"Instruction (BE) : %s | %s\n" +
ANSI_YELLOW + "%s" + ANSI_RESET + "\n",
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
(instructionExpression == 0xd503201f)?"NOP":"Some weird stuff");
}
private static String printTBZ(int instructionExpression, int offset){
int xwSelector = (instructionExpression >> 31 & 1);
int imm = instructionExpression >> 18 & 0b11111;
int Rt = instructionExpression & 0b11111;
int label = (offset + (instructionExpression >> 5 & 0x3fff) * 4) & 0xfffff;
//System.out.printf("\nInstruction: %x\n", instructionExpression);
return String.format(ANSI_YELLOW + "sf == 0 && hw == 0x ? <Wt> else <Xt>\n" +
"TBZ <?t>,#<imm>, <label> |.....TBZ signature.......|\n" +
ANSI_CYAN+" b5 0 1 1 0 1 1 0 |b40.............|imm14.........................................||Rt.............|" + ANSI_RESET + "\n" +
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
"Instruction (BE) : %s | %s\n" +
ANSI_YELLOW + "TBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ", " + ANSI_PURPLE + "%x" + ANSI_RESET + "\n",
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
(xwSelector == 0) ? "w" : "x", Rt, imm, label);
}
private static String printBConditional(int instructionExpression, int offset){
int conditionalJumpLocation = ((instructionExpression >> 4 & 0b1111111111111111111) * 4 + offset) & 0xfffff;
return String.format(
ANSI_YELLOW+"B.%s <label> |...B.cond signature.......|\n" +
ANSI_CYAN+" 0 1 0 1 0 1 0 0 |imm19..........................................................| 0 |.condit...|" + ANSI_RESET + "\n" +
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
"Instruction (BE) : %s | %s\n" +
ANSI_YELLOW + "B.%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
getBConditionalMarker(instructionExpression & 0xf),
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
getBConditionalMarker(instructionExpression & 0xf),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
private static String printUnknown(int instructionExpression){
return String.format(ANSI_RED + " 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
"Instruction (BE) : %s | %s\n",
intAsBinString(instructionExpression), intAsHexString(instructionExpression));
}
private static String getBConditionalMarker(int cond){
switch (cond){
case 0b0000: return "EQ";
case 0b0001: return "NE";
case 0b0010: return "CS";
case 0b0011: return "CC";
case 0b0100: return "MI";
case 0b0101: return "PL";
case 0b0110: return "VS";
case 0b0111: return "VC";
case 0b1000: return "HI";
case 0b1001: return "LS";
case 0b1010: return "GE";
case 0b1011: return "LT";
case 0b1100: return "GT";
case 0b1101: return "LE";
case 0b1110: return "AL";
default: return "??";
}
/*
"__________________CheatSheet_____________________________________\n"+
"0000 | EQ | Z set | equal\n"+
"0001 | NE | Z clear | not equal\n"+
"0010 | CS | C set | unsigned higher or same\n"+
"0011 | CC | C clear | unsigned lower\n"+
"0100 | MI | N set | negative\n"+
"0101 | PL | N clear | positive or zero\n"+
"0110 | VS | V set | overflow\n"+
"0111 | VC | V clear | no overflow\n"+
"1000 | HI | C set & V clear | unsigned higher\n"+
"1001 | LS | C clear or Z set | unsigned lower or same\n"+
"1010 | GE | N equals V | greater or equal\n"+
"1011 | LT | N not equals V | less than\n"+
"1100 | GT | Z clear AND (N equals V) | greater that\n"+
"1101 | LE | Z set OR (N not equals V) | less than or equal\n"+
"1110 | AL | (ignored) | always\n";
*/
}
private static String printCBZSimplified(int instructionExpression, int offset){
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (" + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
private static String printCBNZSimplified(int instructionExpression, int offset){
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CBNZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (" + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
private static String printBSimplified(int instructionExpression, int offset){
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " B " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
}
private static String printBLSimplified(int instructionExpression, int offset){
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " BL " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
}
private static String printMOVSimplified(int instructionExpression, int offset){
int imm16 = instructionExpression >> 5 & 0xFFFF;
int sfHw = (instructionExpression >> 22 & 1);
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
(sfHw == 0) ? "w" : "x", (instructionExpression & 0b11111), imm16);
}
private static String printNOPSimplified(int instructionExpression, int offset){
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " NOP " + ANSI_RESET + "\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
}
private static String printTBZSimplified(int instructionExpression, int offset){
int xwSelector = (instructionExpression >> 31 & 1);
int imm = instructionExpression >> 18 & 0b11111;
int Rt = instructionExpression & 0b11111;
int label = (offset + (instructionExpression >> 5 & 0x3fff) * 4) & 0xfffff;
//System.out.printf("\nInstruction: %x\n", instructionExpression);
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " TBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ", " + ANSI_PURPLE + "%x" + ANSI_RESET + "\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
(xwSelector == 0) ? "w" : "x", Rt, imm, label);
}
private static String printBConditionalSimplified(int instructionExpression, int offset){
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",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
getBConditionalMarker(instructionExpression & 0xf),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
private static String printADDSimplified(int instructionExpression, int offset){ //ADD (immediate)
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " ADD . . . \n"+ ANSI_RESET,
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
}
private static String printLDPSimplified(int instructionExpression, int offset){
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDP . . . \n"+ ANSI_RESET,
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
}
private static String printLDURBSimplified(int instructionExpression, int offset){
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDURB . . . \n"+ ANSI_RESET,
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
}
private static String printSUBSimplified(int instructionExpression, int offset){
String wx = (instructionExpression >> 31 == 0) ? "W" : "X";
int Rt = instructionExpression & 0x1f;
int Rn = instructionExpression >> 5 & 0x1F;
int imm12 = instructionExpression >> 10 & 0xFFF; // unsigned only
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " SUB (imm) " + ANSI_GREEN + "%s%d, " + ANSI_BLUE + "%s%d, #0x%x" + ANSI_RESET + "\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
wx, Rt, wx, Rn, imm12);
}
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;
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV (reg) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
sfHw, Rm, sfHw, Rd);
}
private static String printCMNSimplified(int instructionExpression, int offset){
int Rn = instructionExpression >> 5 & 0x1F;
int imm = instructionExpression >> 10 & 0xFFF;
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMN " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
(instructionExpression >> 31 == 0) ? "w" : "x", Rn, imm);
}
private static String printLRDImmUnsignSimplified(int instructionExpression, int offset){
String wx = (instructionExpression >> 31 == 0) ? "W" : "X";
int Rt = instructionExpression & 0x1f;
int Rn = instructionExpression >> 5 & 0xF;
int imm12 = (instructionExpression >> 10 & 0xFFF) * 8; // unsigned only
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDR(imm) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "[%s%d, #0x%x]" + ANSI_RESET + " (note: unsigned offset)\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
wx, Rt, wx, Rn, imm12);
}
private static String printLRDBImmUnsignSimplified(int instructionExpression, int offset){
String wx = (instructionExpression >> 31 == 0) ? "W" : "X";
int Rt = instructionExpression & 0x1f;
int Rn = instructionExpression >> 5 & 0xF;
int imm12 = (instructionExpression >> 10 & 0xFFF) * 8; // unsigned only
return String.format(
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDRB(imm) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "[%s%d, #0x%x]" + ANSI_RESET + " (note: unsigned offset)\n",
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
wx, Rt, wx, Rn, imm12);
}
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",
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
}
private static String intAsBinString(int number) {
StringBuilder result = new StringBuilder();
for(int i = 31; i >= 0 ; i--) {
int mask = 1 << i;
result.append((number & mask) != 0 ? "1" : "0");
result.append(" ");
if (i % 4 == 0)
result.append(" ");
}
result.replace(result.length() - 1, result.length(), "");
return result.toString();
}
private static String intAsHexString(int number) {
number = Integer.reverseBytes(number);
StringBuilder result = new StringBuilder();
for(int i = 0; i <= 3 ; i++) {
int mask = 0xff << i*8;
result.append(String.format("%02x", (byte)((number & mask) >> i*8)));
result.append(" ");
}
result.replace(result.length() - 1, result.length(), "");
return result.toString();
}
}

View file

@ -0,0 +1,50 @@
/*
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;
import libKonogonka.Converter;
import libKonogonka.Tools.NCA.NCAProvider;
import java.util.List;
import java.util.concurrent.Callable;
class EsNcaSearchTask implements Callable<NCAProvider> {
private final List<NCAProvider> ncaProviders;
EsNcaSearchTask(List<NCAProvider> ncaProviders){
this.ncaProviders = ncaProviders;
}
@Override
public NCAProvider call() {
try {
for (NCAProvider ncaProvider : ncaProviders) {
String titleId = Converter.byteArrToHexStringAsLE(ncaProvider.getTitleId());
if (titleId.startsWith("0100000000000033") && ncaProvider.getContentType() == 0) {
return ncaProvider;
}
}
return null;
}
catch (Exception e){
e.printStackTrace();
return null;
}
}
}

View file

@ -0,0 +1,174 @@
/*
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/>.
---
Based on ES-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.es;
import libKonogonka.Converter;
import libKonogonka.Tools.NCA.NCAProvider;
import libKonogonka.Tools.NSO.NSO0Header;
import libKonogonka.Tools.NSO.NSO0Provider;
import nsusbloader.ModelControllers.ILogPrinter;
import nsusbloader.NSLDataTypes.EMsgType;
import nsusbloader.Utilities.patches.es.finders.HeuristicEsWizard;
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.util.Arrays;
public class EsPatch {
private final NCAProvider ncaProvider;
private final String saveToLocation;
private final ILogPrinter logPrinter;
private Long fwVersion;
private String buildId;
private byte[] _textSection;
private HeuristicEsWizard wizard;
EsPatch(NCAProvider ncaProvider, String saveToLocation, ILogPrinter logPrinter) throws Exception{
this.ncaProvider = ncaProvider;
this.saveToLocation = saveToLocation + File.separator +
"atmosphere" + File.separator + "exefs_patches" + File.separator + "es_patches";
this.logPrinter = logPrinter;
getPlainFirmwareVersion();
NSO0Provider nso0Provider = new NSO0Provider(ncaProvider.getNCAContentProvider(0).getPfs0().getStreamProducer(0));
getBuildId(nso0Provider);
getTextSection(nso0Provider);
findAllOffsets();
mkDirs();
writeFile();
logPrinter.print(" == Debug information ==\n"+wizard.getDebug(), EMsgType.NULL);
}
private void getPlainFirmwareVersion() throws Exception{
fwVersion = Long.parseLong(""+ncaProvider.getSdkVersion()[3]+ncaProvider.getSdkVersion()[2]
+ncaProvider.getSdkVersion()[1] +ncaProvider.getSdkVersion()[0]);
logPrinter.print("Internal firmware version: "+ncaProvider.getSdkVersion()[3] +"."+ncaProvider.getSdkVersion()[2] +"."+ncaProvider.getSdkVersion()[1] +"."+ncaProvider.getSdkVersion()[0], EMsgType.INFO);
}
private void getBuildId(NSO0Provider nso0Provider) throws Exception{
NSO0Header nso0DecompressedHeader = nso0Provider.getAsDecompressedNSO0().getHeader();
byte[] buildIdBytes = nso0DecompressedHeader.getModuleId();
buildId = Converter.byteArrToHexStringAsLE(buildIdBytes).substring(0, 40).toUpperCase();
logPrinter.print("Build ID: "+buildId, EMsgType.INFO);
}
private void getTextSection(NSO0Provider nso0Provider) throws Exception{
_textSection = nso0Provider.getAsDecompressedNSO0().getTextRaw();
}
private void findAllOffsets() throws Exception{
this.wizard = new HeuristicEsWizard(fwVersion, _textSection);
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 + buildId + ".ips";
int offset1 = wizard.getOffset1();
int offset2 = wizard.getOffset2();
int offset3 = wizard.getOffset3();
ByteBuffer handyEsPatch = ByteBuffer.allocate(0x23).order(ByteOrder.LITTLE_ENDIAN);
handyEsPatch.put(getHeader());
if (offset1 > 0) {
logPrinter.print("Patch component 1 will be used", EMsgType.PASS);
handyEsPatch.put(getPatch1(offset1));
}
if (offset2 > 0) {
logPrinter.print("Patch component 2 will be used", EMsgType.PASS);
handyEsPatch.put(getPatch2(offset2));
}
if (offset3 > 0) {
logPrinter.print("Patch component 3 will be used", EMsgType.PASS);
handyEsPatch.put(getPatch3(offset3));
}
handyEsPatch.put(getFooter());
try (BufferedOutputStream stream = new BufferedOutputStream(
Files.newOutputStream(new File(patchFileLocation).toPath()))){
stream.write(handyEsPatch.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);
}
// WE EXPECT TO SEE CBZ (for patch 1) INSTRUCTION RIGHT BEFORE FOUND SEQUENCE (requiredInstructionOffsetInternal)
// IN RESULTING FILE InstructionOffset SHOULD BE INCREMENTED by 0x100 to get real offset
// (because header for decompressed NSO0 size = 0x100; it's fixed alignment produced by libKonogonka)
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{
final int NopExpression = 0x1F2003D5; // reversed
int offsetReal = offset - 4 + 0x100;
logPrinter.print(BinToAsmPrinter.printSimplified(Integer.reverseBytes(NopExpression), offset - 4), EMsgType.NULL);
ByteBuffer prePatch = ByteBuffer.allocate(10).order(ByteOrder.BIG_ENDIAN)
.putInt(offsetReal)
.putShort((short) 4)
.putInt(NopExpression);
return Arrays.copyOfRange(prePatch.array(), 1, 10);
}
private byte[] getPatch3(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);
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);
}
}

View file

@ -0,0 +1,187 @@
/*
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;
import libKonogonka.KeyChainHolder;
import libKonogonka.Tools.NCA.NCAProvider;
import nsusbloader.ModelControllers.CancellableRunnable;
import nsusbloader.ModelControllers.ILogPrinter;
import nsusbloader.ModelControllers.Log;
import nsusbloader.NSLDataTypes.EModule;
import nsusbloader.NSLDataTypes.EMsgType;
import java.io.File;
import java.util.*;
import java.util.concurrent.*;
public class EsPatchMaker 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 EsPatchMaker(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 ES 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);
}
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());
for (Future<NCAProvider> future : futuresResults){
NCAProvider ncaProvider = future.get();
if (ncaProvider != null) {
makePatches(ncaProvider);
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);
new EsPatch(ncaProvider, saveTo, 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 EsNcaSearchTask(getNextSet(iterator, ncaPerThreadAmount));
subTasks.add(task);
}
Callable<NCAProvider> task = new EsNcaSearchTask(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;
}
}

View file

@ -0,0 +1,161 @@
/*
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;
import libKonogonka.Converter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SimplyFind {
private String what;
private final byte[] where;
private Matcher matcherHex;
private Matcher matcherDot;
private final List<Integer> findings = new ArrayList<>();
private final int statementLength;
private final List<SearchBlock> searchBlocks = new ArrayList<>();
/**
* Abstraction layer for searching patterns like "..CAFE..BE" in bytes array.
* It's 'String' combination of hex values and '.' which stands for unknown value.
* Returns offset of the first symbol.
* */
public SimplyFind(String what, byte[] where){
this.where = where;
if (! what.contains(".")){
doKMPSearch(Converter.hexStringToByteArray(what), 0);
this.statementLength = what.length()/2;
return;
}
this.what = what.replaceAll("\\.", "\\.\\.");
this.statementLength = this.what.length()/2;
buildSearchingSequence();
complexSearch();
}
private void buildSearchingSequence(){
Pattern patternHex = Pattern.compile("[0-9]|[A-F]|[a-f]");
Pattern patternDot = Pattern.compile("\\.");
this.matcherHex = patternHex.matcher(what);
this.matcherDot = patternDot.matcher(what);
int nextDotPos = 0;
int nextHexPos;
while(true){
nextHexPos = getNextNumberPosition(nextDotPos);
if (nextHexPos == -1)
break;
nextDotPos = getNextDotPosition(nextHexPos);
if (nextDotPos == -1) {
searchBlocks.add(new SearchBlock(what.substring(nextHexPos), nextHexPos));
break;
}
String searchStatement = what.substring(nextHexPos, nextDotPos);
searchBlocks.add(new SearchBlock(searchStatement, nextHexPos));
}
}
private int getNextNumberPosition(int since){
if (matcherHex.find(since))
return matcherHex.start();
return -1;
}
private int getNextDotPosition(int since){
if (matcherDot.find(since))
return matcherDot.start();
return -1;
}
private void complexSearch(){
SearchBlock block = searchBlocks.get(0);
doKMPSearch(block.statement, block.offsetInStatement);
findings.removeIf(this::searchForward);
}
private boolean searchForward(int offset){
for (int i = 1; i < searchBlocks.size(); i++) {
SearchBlock block = searchBlocks.get(i);
if (! doDumbSearch(block.statement, offset+block.offsetInStatement)){
return true;
}
}
return false;
}
private void doKMPSearch(byte[] subject, int skip){
int whereSize = where.length;
int subjectSize = subject.length;
int[] pf = new int[subjectSize];
int j = 0;
for (int i = 1; i < subjectSize; i++ ) {
while ((j > 0) && (subject[j] != subject[i]))
j = pf[j-1];
if (subject[j] == subject[i])
j++;
pf[i] = j;
}
j = 0;
for (int i = 0; i < whereSize; i++){
while ((j > 0) && (subject[j] != where[i]))
j = pf[j - 1];
if (subject[j] == where[i])
j++;
if (j == subjectSize) {
findings.add(i-j+1-skip);
j = 0;
}
}
}
private boolean doDumbSearch(byte[] subject, int since){
for (int i = 0; i < subject.length; i++) {
if (where[since + i] != subject[i])
return false;
}
return true;
}
public int getStatementLength() {
return statementLength;
}
public List<Integer> getResults(){
return findings;
}
private static class SearchBlock{
byte[] statement;
int offsetInStatement;
SearchBlock(String statement, int offset){
if (statement != null) {
this.statement = Converter.hexStringToByteArray(statement);
}
this.offsetInStatement = offset/2;
}
}
}

View file

@ -0,0 +1,109 @@
/*
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;
import libKonogonka.Converter;
import nsusbloader.Utilities.patches.es.BinToAsmPrinter;
import nsusbloader.Utilities.patches.es.SimplyFind;
import java.util.List;
class HeuristicEs1 implements IHeuristicEs {
private static final String PATTERN = "1F90013128.8052";
private final List<Integer> findings;
private final byte[] where;
HeuristicEs1(byte[] where){
this.where = where;
SimplyFind simplyfind = new SimplyFind(PATTERN, where);
this.findings = simplyfind.getResults();
this.findings.removeIf(this::dropStep1);
if(findings.size() < 2)
return;
this.findings.removeIf(this::dropStep2);
}
// Check ranges
private boolean dropStep1(int offsetOfPatternFound){
return ((offsetOfPatternFound < 0x10000 || offsetOfPatternFound > 0xffffc));
}
// Remove non-CBZ
private boolean dropStep2(int offsetOfPatternFound){
return ((where[offsetOfPatternFound - 1] & (byte) 0b01111111) != 0x34);
}
@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();
}
@Override
public String getDetails(){
StringBuilder builder = new StringBuilder();
int cbzOffsetInternal = findings.get(0) - 4;
int instructionExpression = Converter.getLEint(where, cbzOffsetInternal);
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff;
int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation);
int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation+4);
builder.append(BinToAsmPrinter.printSimplified(instructionExpression, 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 int getId(){
return 1;
}
}

View file

@ -0,0 +1,168 @@
/*
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;
import libKonogonka.Converter;
import nsusbloader.Utilities.patches.es.BinToAsmPrinter;
import nsusbloader.Utilities.patches.es.SimplyFind;
import java.util.ArrayList;
import java.util.List;
class HeuristicEs2 implements IHeuristicEs {
private static final String PATTERN = ".D2.52";
private List<Integer> findings;
private final byte[] where;
HeuristicEs2(byte[] where){
this.where = where;
find();
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);
if(findings.size() < 2)
return;
this.findings.removeIf(this::dropStep4);
if(findings.size() < 2)
return;
this.findings.removeIf(this::dropStep5);
if(findings.size() < 2)
return;
this.findings.removeIf(this::dropStep6);
}
private void find(){
SimplyFind simplyfind = new SimplyFind(PATTERN, where);
findings = new ArrayList<>();
// This approach a bit different. We're looking for pattern we want to patch, not the next one.
// So easier way is just to shift every value and pretend that nothing happened.
for (int offset : simplyfind.getResults())
findings.add(offset+4);
}
// Limit range
private boolean dropStep1(int offsetOfPatternFound){
return ((offsetOfPatternFound < 0x10000 || offsetOfPatternFound > 0xffffc));
}
// Is CBNZ next?
private boolean dropStep2(int offsetOfPatternFound){
return ! isCBNZ(Converter.getLEint(where, offsetOfPatternFound));
}
// Check what's above
private boolean dropStep3(int offsetOfPatternFound){
return ! isMOV(Converter.getLEint(where, offsetOfPatternFound-4));
}
// Check what's beyond or after jump
private boolean dropStep4(int offsetOfPatternFound) {
int nextExpression = Converter.getLEint(where, offsetOfPatternFound + 4);
return ! isLDRB_LDURB(nextExpression); // Drop if not LDRB OR LDURB
}
// Check second after jump if LDR-TBZ
private boolean dropStep5(int offsetOfPatternFound) {
int expression = Converter.getLEint(where, offsetOfPatternFound);
int afterJumpPosition = ((expression >> 5 & 0x7FFFF) * 4 + offsetOfPatternFound) & 0xfffff;
int secondAfterJumpExpression = Converter.getLEint(where, afterJumpPosition+4);
return ! isBL(secondAfterJumpExpression); //Second after jump = BL? No -> Drop
}
// Check second after jump if LDR-TBZ
private boolean dropStep6(int offsetOfPatternFound) {
int expression = Converter.getLEint(where, offsetOfPatternFound);
int afterJumpPosition = ((expression >> 5 & 0x7FFFF) * 4 + offsetOfPatternFound) & 0xfffff;
int forthAfterJumpExpression = Converter.getLEint(where, afterJumpPosition+12);
return ! isBL(forthAfterJumpExpression); //Forth after jump = BL? No -> Drop
}
@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();
}
@Override
public String getDetails(){
StringBuilder builder = new StringBuilder();
int secondExpressionOffset = findings.get(0);
int firstExpression = Converter.getLEint(where, secondExpressionOffset-4);
int secondExpression = Converter.getLEint(where, secondExpressionOffset);
int conditionalJumpLocation = 0;
if ((secondExpression >> 24 & 0x7f) == 0x35) {
conditionalJumpLocation = ((secondExpression >> 5 & 0x7FFFF) * 4 + secondExpressionOffset) & 0xfffff;
}
else if ((firstExpression >> 24 & 0x7f) == 0x36) {
conditionalJumpLocation = (secondExpressionOffset-4 + (firstExpression >> 5 & 0x3fff) * 4) & 0xfffff;
}
int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation);
int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation + 4);
int secondExpressionsPairElement3 = Converter.getLEint(where, conditionalJumpLocation + 8);
int secondExpressionsPairElement4 = Converter.getLEint(where, conditionalJumpLocation + 12);
builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, secondExpressionOffset-4), secondExpressionOffset-4));
builder.append(BinToAsmPrinter.printSimplified(secondExpression, secondExpressionOffset));
builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, secondExpressionOffset+4), secondExpressionOffset+4));
builder.append("...\n");
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation));
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation + 4));
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement3, conditionalJumpLocation + 8));
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement4, conditionalJumpLocation + 12));
return builder.toString();
}
@Override
public int getId(){
return 2;
}
}

View file

@ -0,0 +1,151 @@
/*
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;
import libKonogonka.Converter;
import nsusbloader.Utilities.patches.es.BinToAsmPrinter;
import nsusbloader.Utilities.patches.es.SimplyFind;
import java.util.List;
class HeuristicEs3 implements IHeuristicEs {
private static final String PATTERN0 = "..FF97";
private static final String PATTERN1 = "......FF97"; // aka "E0230091..FF97";
private final List<Integer> findings;
private final byte[] where;
HeuristicEs3(long fwVersion, byte[] where){
this.where = where;
String pattern = getPattern(fwVersion);
SimplyFind simplyfind = new SimplyFind(pattern, where);
this.findings = simplyfind.getResults();
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 < 10400)
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();
}
@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();
}
@Override
public int getId(){
return 3;
}
}

View file

@ -0,0 +1,127 @@
/*
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;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class HeuristicEsWizard {
private final List<IHeuristicEs> all;
private final List<IHeuristicEs> found;
private final List<IHeuristicEs> wantLessEntropy;
private final StringBuilder errorsAndNotes;
private int offset1 = -1;
private int offset2 = -1;
private int offset3 = -1;
public HeuristicEsWizard(long fwVersion, byte[] where) throws Exception{
this.errorsAndNotes = new StringBuilder();
this.all = Arrays.asList(
new HeuristicEs1(where),
new HeuristicEs2(where),
new HeuristicEs3(fwVersion, where)
);
this.found = all.stream()
.filter(IHeuristicEs::isFound)
.collect(Collectors.toList());
if (found.isEmpty())
throw new Exception("Nothing found!");
this.wantLessEntropy = all.stream()
.filter(IHeuristicEs::wantLessEntropy)
.collect(Collectors.toList());
shareOffsetsWithEachOther();
assignOffset1();
assignOffset2();
assignOffset3();
}
private void shareOffsetsWithEachOther(){
for (IHeuristicEs es : wantLessEntropy) {
if (shareWithNext(es))
return;
}
}
private boolean shareWithNext(IHeuristicEs es){
try {
for (IHeuristicEs 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"); }
}
private void assignOffset3(){
try {
offset3 = all.get(2).getOffset();
}
catch (Exception e){ errorsAndNotes.append(e.getLocalizedMessage()).append("\n"); }
}
public String getErrorsAndNotes(){
return errorsAndNotes.toString();
}
public String getDebug(){
StringBuilder builder = new StringBuilder();
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());
}
if (all.get(2).isFound()){
builder.append("\t\t-=== 3 ===-\n");
builder.append(all.get(2).getDetails());
}
return builder.toString();
}
public int getOffset1() { return offset1; }
public int getOffset2() { return offset2; }
public int getOffset3() { return offset3; }
}

View file

@ -0,0 +1,47 @@
/*
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);
int getId();
}

View file

@ -18,9 +18,12 @@
*/
package nsusbloader.cli;
import nsusbloader.AppPreferences;
import nsusbloader.Main;
import nsusbloader.NSLMain;
import org.apache.commons.cli.*;
import java.time.LocalTime;
import java.util.prefs.Preferences;
public class CommandLineInterface {
@ -32,10 +35,12 @@ public class CommandLineInterface {
}
final Options cliOptions = createCliOptions();
final Options cliOptionsToBeExact =
createCliOptions().addOption(Option.builder().longOpt("gimmegimmegimme").hasArg(false).build());
CommandLineParser cliParser = new DefaultParser();
try{
CommandLine cli = cliParser.parse(cliOptions, args);
CommandLine cli = cliParser.parse(cliOptionsToBeExact, args);
if (cli.hasOption('v') || cli.hasOption("version")){
handleVersion();
return;
@ -75,6 +80,16 @@ public class CommandLineInterface {
return;
}
*/
if (cli.hasOption("gimmegimmegimme")){
if (LocalTime.now().isBefore(LocalTime.parse("09:00:00"))){
AppPreferences.getInstance().give();
AppPreferences.getInstance().setLastOpenedTab("PatchesTabHolder");
System.out.println("=)");
Main.main(new String[]{});
return;
}
throw new ParseException("Unhandled LocalTime() exception;");
}
if (cli.hasOption("s") || cli.hasOption("split")){
final String[] arguments = cli.getOptionValues("split");
new SplitCli(arguments);