Skip to content

Commit

Permalink
iOS Restrictions Recovery v0.6
Browse files Browse the repository at this point in the history
  • Loading branch information
Emery Ferrari committed Jul 2, 2020
1 parent 420c708 commit d1124b5
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 31 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
# iOS Restrictions Recovery

by Emery Ferrari<br/>
A GUI/command-line tool that will recover the restrictions passcode from a device running iOS 7.0-11.4.1.
A GUI/command-line tool that will recover the restrictions passcode from a device running iOS 7.0-11.4.1, either jailbroken or unjailbroken.

## Credit

slf4j Copyright (c) 2004-2017 QOS.ch<br/>
bc-java Copyright (c) 2000-2019 The Legion of the Bouncy Castle Inc.<br/>
sshj Copyright (c) 2010-2012 sshj contributors
sshj Copyright (c) 2010-2012 sshj contributors<br/><br/>
The idea for the iTunes backup feature was given to me by:<br/>
[u/Starwarsfan2099](https://reddit.com/user/Starwarsfan2099) and<br/>
[u/KuroAMK](https://reddit.com/user/KuroAMK)<br/>
The code for the iTunes backup feature was loosely based on [this GitHub project](https://github.com/Starwarsfan2099/iOS-Restriction-Key-Cracker) by [u/Starwarsfan2099](https://reddit.com/user/Starwarsfan2099)

## Dependencies

Expand All @@ -16,7 +20,7 @@ slf4j (slf4j-api-1.7.2 and slf4j-jdk14-1.7.2 are used for compilation of the rel
sshj (sshj-0.27.0 is used for compilation of the release jars)<br/>
ed25519-java (eddsa-0.3.0 is used for compilation of the release jars)<br/>
bc-java (bcprov-jdk15on-1.64 is used for compilation of the release jars)<br/><br/>
Note: The iproxy feature currently does not work.
Note: The iproxy feature currently does not work.<br/>
To use the iproxy feature:<br/>
-macOS: homebrew, libimobiledevice<br/>
-Unix-based operating systems: libusbmuxd-tools<br/>
Expand All @@ -26,10 +30,6 @@ To use the iproxy feature:<br/>

This tool can either be run from the .jar executable of the latest release in the Releases tab, or can be compiled using javac.

## Known issues

There are many performance and consistency issues in the source code. None of these will affect your experience with the program in practice, however these are planned to be fixed in a future pre-release update nonetheless.

## Contacting me

I will respond to any PM I receive on Reddit.<br/>
Expand Down
145 changes: 126 additions & 19 deletions src/com/emeryferrari/iosrr/Display.java
Original file line number Diff line number Diff line change
@@ -1,61 +1,162 @@
package com.emeryferrari.iosrr;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
public class Display {
private Display() {}
private static final Display CLASS_OBJ = new Display();
private static JFrame FRAME = new JFrame(RRConst.NAME + " - v" + RRConst.VERSION);
private static JFrame FRAME = new JFrame(RRConst.NAME + " " + RRConst.VERSION);
private static JLabel TITLE = new JLabel(RRConst.TITLE);
private static JLabel DESC = new JLabel(RRConst.DESC);
private static JButton KEY_SALT_BUTTON = new JButton(RRConst.KEY_SALT_BUTTON);
private static JButton FILE_BUTTON = new JButton(RRConst.FILE_BUTTON);
private static JButton SSH_BUTTON = new JButton(RRConst.SSH_BUTTON);
private static JButton IPROXY_BUTTON = new JButton(RRConst.IPROXY_BUTTON);
private static JButton ITUNES_BACKUP = new JButton(RRConst.ITUNES_BACKUP);
private static InfoPlist[] plists = null;
private static boolean initialized = false;
public static void createDisplay() {
Display.FRAME.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Display.FRAME.setSize(800, 600);
Display.FRAME.setLayout(new BoxLayout(FRAME.getContentPane(), BoxLayout.Y_AXIS));
Display.KEY_SALT_BUTTON.addActionListener(Display.CLASS_OBJ.new KeySaltButtonListener());
Display.FILE_BUTTON.addActionListener(Display.CLASS_OBJ.new FileButtonListener());
Display.SSH_BUTTON.addActionListener(Display.CLASS_OBJ.new SSHButtonListener());
Display.IPROXY_BUTTON.addActionListener(Display.CLASS_OBJ.new IproxyButtonListener());
Display.IPROXY_BUTTON.setEnabled(false);
if (!initialized) {
Display.FRAME.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Display.FRAME.setSize(800, 600);
Display.FRAME.setLayout(new BoxLayout(FRAME.getContentPane(), BoxLayout.Y_AXIS));
Display.KEY_SALT_BUTTON.addActionListener(Display.CLASS_OBJ.new KeySaltButtonListener());
Display.FILE_BUTTON.addActionListener(Display.CLASS_OBJ.new FileButtonListener());
Display.SSH_BUTTON.addActionListener(Display.CLASS_OBJ.new SSHButtonListener());
Display.IPROXY_BUTTON.addActionListener(Display.CLASS_OBJ.new IproxyButtonListener());
Display.IPROXY_BUTTON.setEnabled(false);
Display.ITUNES_BACKUP.addActionListener(Display.CLASS_OBJ.new ItunesBackupListener());
initialized = true;
}
Display.FRAME.getContentPane().add(Display.TITLE);
Display.FRAME.getContentPane().add(Display.DESC);
Display.FRAME.getContentPane().add(Display.KEY_SALT_BUTTON);
Display.FRAME.getContentPane().add(Display.FILE_BUTTON);
Display.FRAME.getContentPane().add(Display.SSH_BUTTON);
Display.FRAME.getContentPane().add(Display.IPROXY_BUTTON);
Display.FRAME.getContentPane().add(Display.ITUNES_BACKUP);
Display.FRAME.setVisible(true);
}
public class ItunesBackupListener implements ActionListener {
public void actionPerformed(ActionEvent ev) {
try {
OperatingSystem currentOS = RestrictionsRecovery.identifyHostOS();
String backupPath = System.getProperty("user.home") + "\\";
if (currentOS == OperatingSystem.WINDOWS) {
String y = JOptionPane.showInputDialog("Are you using the Microsoft Store version of iTunes? (y/n)");
if (y.equalsIgnoreCase("y") || y.equalsIgnoreCase("yes")) {
backupPath += "Apple\\MobileSync\\Backup\\";
} else {
backupPath += "AppData\\Roaming\\Apple Computer\\MobileSync\\Backup\\";
}
} else if (currentOS == OperatingSystem.MACOSMOJAVE || currentOS == OperatingSystem.MACOSCATALINA) {

} else {
throw new Exception("Your OS is not supported because it is not possible to install iTunes on your OS.");
}
File backupLocation = new File(backupPath);
String[] backups = backupLocation.list(new FilenameFilter() {
@Override
public boolean accept(File current, String name) {
return new File(current, name).isDirectory();
}
});
Display.FRAME.getContentPane().removeAll();
Display.FRAME.getContentPane().add(new JLabel(RRConst.ITUNES_BACKUPS));
plists = new InfoPlist[backups.length];
for (int i = 0; i < backups.length; i++) {
InfoPlist plist = InfoPlist.getInstance(backupPath + backups[i] + "\\");
plists[i] = plist;
if (plist != null) {
JButton button = new JButton(plist.getDeviceName() + " / " + plist.getiOSVersion() + " / " + plist.getBackupDate() + " / " + plist.getDisplayName());
if (plist.getiOSRelease() < 7 || plist.getiOSRelease() > 11) {
button.setEnabled(false);
}
button.addActionListener(Display.CLASS_OBJ.new BackupListener(i));
Display.FRAME.getContentPane().add(button);
}
}
JButton back = new JButton("Back");
back.addActionListener(new BackListener());
Display.FRAME.getContentPane().add(back);
Display.refresh();
} catch (Exception ex) {
Display.handleException(ex, true);
}
}
}
public class BackupListener implements ActionListener {
private int index;
public BackupListener(int index) {
this.index = index;
}
public void actionPerformed(ActionEvent ev) {
try {
KeySaltPair pair = PropertyListReader.getKeyAndSaltFromPlist(plists[index].getFile() + "398bc9c2aeeab4cb0c12ada0f52eea12cf14f40b");
Display.FRAME.getContentPane().add(new JLabel("<html><body><strong>Calculating passcode...</strong></body></html>"));
Display.refresh();
String passcode = RestrictionsRecovery.calculate(pair.getKey(), pair.getSalt(), false);
if (passcode == null) {
throw new Exception("Passcode could not be found. Key and salt does not correspond to any passcode between 0000 and 9999.");
} else {
JOptionPane.showMessageDialog(null, "Passcode: " + passcode);
}
Display.FRAME.getContentPane().removeAll();
Display.createDisplay();
Display.refresh();
} catch (Exception ex) {
Display.handleException(ex, true);
}
}
}
public static void refresh() {
Display.FRAME.revalidate();
Display.FRAME.repaint();
}
public static class BackListener implements ActionListener {
public void actionPerformed(ActionEvent ev) {
Display.FRAME.getContentPane().removeAll();
Display.createDisplay();
Display.refresh();
}
}
public class KeySaltButtonListener implements ActionListener {
public void actionPerformed(ActionEvent ev) {
try {
String key = JOptionPane.showInputDialog("Key?");
String salt = JOptionPane.showInputDialog("Salt?");
Display.DESC.setText("<html><b>Calculating passcode...</b></html>");
Display.refresh();
String passcode = RestrictionsRecovery.calculate(key, salt, false);
if (passcode == null) {
throw new Exception();
throw new Exception("Passcode could not be found. Key and salt does not correspond to any passcode between 0000 and 9999.");
} else {
JOptionPane.showMessageDialog(null, "Passcode: " + passcode);
}
} catch (Exception ex) {
Display.handleException(ex);
Display.handleException(ex, true);
}
Display.DESC.setText(RRConst.DESC);
Display.refresh();
}
}
public class FileButtonListener implements ActionListener {
public void actionPerformed(ActionEvent ev) {
try {
String file = JOptionPane.showInputDialog("Property list path?");
String file = JOptionPane.showInputDialog("Property list path? (Do not include quotes)");
KeySaltPair pair = PropertyListReader.getKeyAndSaltFromPlist(file);
Display.DESC.setText("<html><b>Calculating passcode...</b></html>");
Display.refresh();
String passcode = RestrictionsRecovery.calculate(pair.getKey(), pair.getSalt(), false);
if (passcode == null) {
throw new Exception();
throw new Exception("Passcode could not be found. Key and salt does not correspond to any passcode between 0000 and 9999.");
} else {
JOptionPane.showMessageDialog(null, "Passcode: " + passcode);
}
} catch (Exception ex) {
Display.handleException(ex);
Display.handleException(ex, true);
}
Display.DESC.setText(RRConst.DESC);
Display.refresh();
}
}
public class SSHButtonListener implements ActionListener {
Expand All @@ -64,26 +165,32 @@ public void actionPerformed(ActionEvent ev) {
String ip = JOptionPane.showInputDialog("Device IP address?");
String password = JOptionPane.showInputDialog("Device root password?");
String port = JOptionPane.showInputDialog("Device SSH port?");
Display.DESC.setText("<html><b>Calculating passcode...</b></html>");
Display.refresh();
RestrictionsRecovery.downloadViaSSH(ip, Integer.parseInt(port), password, false);
KeySaltPair pair = PropertyListReader.getKeyAndSaltFromPlist("password.plist");
String passcode = RestrictionsRecovery.calculate(pair.getKey(), pair.getSalt(), false);
if (passcode == null) {
throw new Exception();
throw new Exception("Passcode could not be found. Key and salt does not correspond to any passcode between 0000 and 9999.");
} else {
JOptionPane.showMessageDialog(null, "Passcode: " + passcode);
}
} catch (Exception ex) {
Display.handleException(ex);
Display.handleException(ex, true);
}
Display.DESC.setText(RRConst.DESC);
Display.refresh();
}
}
public class IproxyButtonListener implements ActionListener {
public void actionPerformed(ActionEvent ev) {
// not implemented yet
}
}
private static void handleException(Exception ex) {
private static void handleException(Exception ex, boolean message) {
ex.printStackTrace();
JOptionPane.showMessageDialog(null, "Error occurred while calculating the passcode. Check the console for details.");
if (message) {
JOptionPane.showMessageDialog(null, "Error: " + ex.getClass().toString().split(" ")[1] + ": " + ex.getMessage());
}
}
}
67 changes: 67 additions & 0 deletions src/com/emeryferrari/iosrr/InfoPlist.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.emeryferrari.iosrr;
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class InfoPlist {
private final String deviceName;
private final String displayName;
private final String backupDate;
private final String iOSVersion;
private final String file;
private InfoPlist(String deviceName, String displayName, String backupDate, String iOSVersion, String file) {
this.deviceName = deviceName;
this.displayName = displayName;
this.backupDate = backupDate;
this.iOSVersion = iOSVersion;
this.file = file;
}
public String getDeviceName() {
return deviceName;
}
public String getDisplayName() {
return displayName;
}
public String getBackupDate() {
return backupDate;
}
public String getiOSVersion() {
return iOSVersion;
}
public String getFile() {
return file;
}
public int getiOSRelease() {
String version = iOSVersion.split(" ")[1].substring(0, 2);
if (version.endsWith(".")) {
return Integer.parseInt(version.substring(0, 1));
}
return Integer.parseInt(version);
}
public static InfoPlist getInstance(String file) {
try {
String fileTemp = file + "Info.plist";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(fileTemp);
document.getDocumentElement().normalize();
NodeList nodes = document.getElementsByTagName("string");
String displayName = nodes.item(1).getTextContent();
boolean stop = false;
String deviceName = null;
String iOSVersion = null;
for (int i = 4; !stop; i++) {
if (nodes.item(i).getTextContent().startsWith("iP")) {
deviceName = nodes.item(i+1).getTextContent();
iOSVersion = "iOS " + nodes.item(i+2).getTextContent();
stop = true;
}
}
String backupDate = document.getElementsByTagName("date").item(0).getTextContent();
String backupDate1 = backupDate.substring(0, 10);
String backupDate2 = backupDate.substring(14, 19);
backupDate = backupDate1 + "@" + backupDate2;
return new InfoPlist(deviceName, displayName, backupDate, iOSVersion, file);
} catch (Exception ex) {
return null;
}
}
}
3 changes: 1 addition & 2 deletions src/com/emeryferrari/iosrr/PropertyListReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
public class PropertyListReader {
private PropertyListReader() {}
public static KeySaltPair getKeyAndSaltFromPlist(String plist) throws ParserConfigurationException, IOException, SAXException {
File plistFile = new File(plist);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(plistFile);
Document document = builder.parse(plist);
document.getDocumentElement().normalize();
String key = document.getElementsByTagName("data").item(0).getTextContent().split(" ")[1].split("\n")[0];
String salt = document.getElementsByTagName("data").item(1).getTextContent().split(" ")[1].split("\n")[0];
Expand Down
9 changes: 6 additions & 3 deletions src/com/emeryferrari/iosrr/RRConst.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
public class RRConst {
private RRConst() {}
public static final String NAME = "iOS-Restrictions-Recovery";
public static final String VERSION = "0.5";

public static final String TITLE = "<html><body><font size=\"10\">iOS-Restrictions-Recovery</font></body></html>";
public static final String VERSION = "v0.6";
public static final String TITLE = "<html><body><font size=\"6\">" + NAME + " " + VERSION + "</font></body></html>";
public static final String DESC = "<html><body>Compatible <strong>only</strong> with <strong>iOS 7.0</strong> through <strong>iOS 11.4.1</strong></body></html>";
public static final String KEY_SALT_BUTTON = "From key and salt";
public static final String FILE_BUTTON = "From property list file";
public static final String SSH_BUTTON = "From device via SSH";
public static final String IPROXY_BUTTON = "From device via iproxy over USB";
public static final String ITUNES_BACKUP = "From unencrypted iTunes backup";
public static final String ITUNES_BACKUPS = "iTunes Backups:";
public static final String ITUNES_BACKUP_12 = "From encrypted iTunes backup (iOS 12 only)";
}
2 changes: 2 additions & 0 deletions src/com/emeryferrari/iosrr/RestrictionsRecovery.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ public static OperatingSystem identifyHostOS() {
} else if (os.indexOf("mac") >= 0) {
if (System.getProperty("os.version").indexOf("10.15") >= 0) {
return OperatingSystem.MACOSCATALINA;
} else if (System.getProperty("os.version").indexOf("11.") >= 0) {
return OperatingSystem.MACOSCATALINA;
} else {
return OperatingSystem.MACOSMOJAVE;
}
Expand Down

0 comments on commit d1124b5

Please sign in to comment.