diff --git a/.classpath b/.classpath
index 6677567..27f6b5c 100644
--- a/.classpath
+++ b/.classpath
@@ -1,11 +1,21 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
index dd858b4..cef071e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,4 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
/bin/
+/target/
diff --git a/.project b/.project
index 65a1408..fa80926 100644
--- a/.project
+++ b/.project
@@ -10,8 +10,14 @@
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+ org.eclipse.m2e.core.maven2Natureorg.eclipse.jdt.core.javanature
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..2f5cc74
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/Keychain-Dumper_LICENSE b/Keychain-Dumper_LICENSE
new file mode 100644
index 0000000..40a794b
--- /dev/null
+++ b/Keychain-Dumper_LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2011, Neohapsis, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
index 55953fa..a402c56 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,14 @@
-# iOS Restrictions Recovery
+# iOS-Restrictions-Recovery
-by Emery Ferrari
-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.
+by Alyx Ferrari
+A GUI/CLI tool that can find the Restrictions or Screen Time passcode of any iOS device running iOS 7.0 through iOS 13.x.
## Credit
slf4j Copyright (c) 2004-2017 QOS.ch
bc-java Copyright (c) 2000-2019 The Legion of the Bouncy Castle Inc.
sshj Copyright (c) 2010-2012 sshj contributors
+[keychain_dumper](https://github.com/ptoomey3/Keychain-Dumper/) was written by [ptoomey3](https://github.com/ptoomey3/).
The idea for the iTunes backup feature was given to me by:
[u/Starwarsfan2099](https://reddit.com/user/Starwarsfan2099) and
[u/KuroAMK](https://reddit.com/user/KuroAMK)
@@ -15,22 +16,27 @@ The code for the iTunes backup feature was loosely based on [this GitHub project
## Dependencies
-To compile:
-slf4j (slf4j-api-1.7.2 and slf4j-jdk14-1.7.2 are used for compilation of the release jars)
-sshj (sshj-0.27.0 is used for compilation of the release jars)
-ed25519-java (eddsa-0.3.0 is used for compilation of the release jars)
-bc-java (bcprov-jdk15on-1.64 is used for compilation of the release jars)
-Note: The iproxy feature currently does not work.
-To use the iproxy feature:
- -macOS: homebrew, libimobiledevice
- -Unix-based operating systems: libusbmuxd-tools
- -Windows: Must have iproxy in your PATH environment variable
+All dependencies are handled by Maven.
+sshj
+slf4j
+ed25519>
+bcprov-jdk15on
+bcpkix-jdk15on
+jzlib
## Compilation/Execution
-This tool can either be run from the .jar executable of the latest release in the Releases tab, or can be compiled using javac.
+To run this program, you can either download the JAR from the Releases tab or generate one yourself with Maven.
+Whether you use the Releases JAR or one generated yourself, if you want to use the iOS 12-13 features, keychain_dumper must be in the same directory as the JAR.
+OpenSSH is required to use the iOS 12-13 features and the iOS 7.0-11.4.1 SSH features. If you're using checkra1n, iproxy will work as an alternative. If you're using any other jailbreak, OpenSSH is available on the default repos.
## Contacting me
I will respond to any PM I receive on Reddit.
[u/verystrangebeing](https://reddit.com/user/verystrangebeing/)
+
+## Donate
+
+All of my work is free and open-source. A donation would be greatly appreciated!
+[Buy me a coffee!](buymeacoff.ee/alyxferrari/)
+[PayPal](paypal.me/alyxferrari/)
diff --git a/keychain_dumper b/keychain_dumper
new file mode 100644
index 0000000..c0487c2
Binary files /dev/null and b/keychain_dumper differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..99c391c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,48 @@
+
+ 4.0.0
+ com.alyxferrari
+ iosrr
+ 0.7
+ iOS-Restrictions-Recovery
+ jar
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.66
+
+
+ net.i2p.crypto
+ eddsa
+ 0.3.0
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.30
+
+
+ org.slf4j
+ slf4j-jdk14
+ 1.7.30
+
+
+ com.hierynomus
+ sshj
+ 0.29.0
+
+
+
+ src
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+
+ 1.8
+
+
+
+
+
\ No newline at end of file
diff --git a/src/com/emeryferrari/iosrr/Display.java b/src/com/emeryferrari/iosrr/Display.java
index 929c241..0106c0d 100644
--- a/src/com/emeryferrari/iosrr/Display.java
+++ b/src/com/emeryferrari/iosrr/Display.java
@@ -2,6 +2,10 @@
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
+import net.schmizz.sshj.*;
+import net.schmizz.sshj.common.*;
+import net.schmizz.sshj.transport.verification.*;
+import net.schmizz.sshj.connection.channel.direct.*;
public class Display {
private Display() {}
private static final Display CLASS_OBJ = new Display();
@@ -13,16 +17,18 @@ private Display() {}
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 JButton KEYCHAIN_DUMPER = new JButton(RRConst.KEYCHAIN_DUMPER);
private static InfoPlist[] plists = null;
private static boolean initialized = false;
public static void createDisplay() {
if (!initialized) {
Display.FRAME.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- Display.FRAME.setSize(800, 600);
+ Display.FRAME.setSize(550, 400);
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.KEYCHAIN_DUMPER.addActionListener(Display.CLASS_OBJ.new KeychainDumperListener());
Display.IPROXY_BUTTON.addActionListener(Display.CLASS_OBJ.new IproxyButtonListener());
Display.IPROXY_BUTTON.setEnabled(false);
Display.ITUNES_BACKUP.addActionListener(Display.CLASS_OBJ.new ItunesBackupListener());
@@ -33,9 +39,112 @@ public static void createDisplay() {
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.KEYCHAIN_DUMPER);
Display.FRAME.getContentPane().add(Display.ITUNES_BACKUP);
Display.FRAME.setVisible(true);
}
+ public class KeychainDumperListener implements ActionListener {
+ public void actionPerformed(ActionEvent ev) {
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ int confirm = JOptionPane.showConfirmDialog(null, "This feature requires SQLite 3.x by Sam Bingner to be installed on your device. Do you consent to automatic installation of this package on your device?");
+ if (confirm == 0) {
+ String ip = JOptionPane.showInputDialog("Device IP address? OpenSSH must be installed on your device.");
+ String portStr = JOptionPane.showInputDialog("Device SSH server port? (press enter to default to 22)");
+ int port = 22;
+ if (!portStr.equals("")) {
+ port = Integer.parseInt(portStr);
+ }
+ String rootPass = JOptionPane.showInputDialog("What is your device's root password? (press enter to default to 'alpine')");
+ if (rootPass.equals("")) {
+ rootPass = "alpine";
+ }
+ Display.FRAME.getContentPane().removeAll();
+ Display.FRAME.getContentPane().add(new JLabel("Connecting to " + ip + ":" + port + " over SSH..."));
+ Display.refresh();
+ System.out.println("Connecting to " + ip + ":" + port + " over SSH...");
+ SSHClient ssh = new SSHClient();
+ ssh.addHostKeyVerifier(new PromiscuousVerifier());
+ ssh.connect(ip, port);
+ Display.FRAME.getContentPane().add(new JLabel("Logging in as user 'root'..."));
+ System.out.println("Logging in as user 'root'...");
+ Display.refresh();
+ ssh.authPassword("root", rootPass);
+ Display.FRAME.getContentPane().add(new JLabel("Uploading keychain_dumper to device..."));
+ Display.refresh();
+ System.out.println("Uploading keychain_dumper to device...");
+ ssh.newSCPFileTransfer().upload("keychain_dumper", "/User/Documents/keychain_dumper");
+ Session session = ssh.startSession();
+ Display.FRAME.getContentPane().add(new JLabel("Giving keychain_dumper '+x' permissions..."));
+ System.out.println("Giving keychain_dumper '+x' permissions...");
+ Display.refresh();
+ session.exec("chmod +x /User/Documents/keychain_dumper");
+ Display.FRAME.getContentPane().add(new JLabel("Installing SQLite 3.x by Sam Bingner to the device..."));
+ System.out.println("Installing SQLite 3.x by Sam Bingner to the device...");
+ Display.refresh();
+ session = ssh.startSession();
+ session.exec("apt install sqlite3");
+ Display.FRAME.getContentPane().add(new JLabel("Disconnecting..."));
+ System.out.println("Disconnecting...");
+ Display.refresh();
+ ssh.disconnect();
+ ssh.close();
+ SSHClient ssh2 = new SSHClient();
+ ssh2.addHostKeyVerifier(new PromiscuousVerifier());
+ Display.FRAME.getContentPane().add(new JLabel("Reconnecting to " + ip + ":" + port + "..."));
+ System.out.println("Reconnecting to " + ip + ":" + port + "...");
+ Display.refresh();
+ ssh2.connect(ip, port);
+ Display.FRAME.getContentPane().add(new JLabel("Logging in as user 'root'..."));
+ System.out.println("Logging in as user 'root'...");
+ Display.refresh();
+ ssh2.authPassword("root", rootPass);
+ Session session2 = ssh2.startSession();
+ JOptionPane.showMessageDialog(null, "Please make sure your device is unlocked and on the home screen.");
+ Display.FRAME.getContentPane().add(new JLabel("Dumping your device's Keychain... (if this blocks, make sure your device is unlocked)"));
+ System.out.println("Dumping your device's Keychain... (if this blocks, make sure your device is unlocked)");
+ Display.refresh();
+ Session.Command cmd = session2.exec("./../mobile/Documents/keychain_dumper");
+ String keychain = IOUtils.readFully(cmd.getInputStream()).toString();
+ Display.FRAME.getContentPane().add(new JLabel("Disconnecting..."));
+ System.out.println("Disconnecting...");
+ Display.refresh();
+ ssh2.disconnect();
+ ssh2.close();
+ Display.FRAME.getContentPane().add(new JLabel("Parsing Keychain dump..."));
+ System.out.println("Parsing Keychain dump...");
+ Display.refresh();
+ String[] list = keychain.split("ParentalControls")[1].split("\n");
+ String password = null;
+ for (int i = 0; i < 20; i++) {
+ if (list[i].startsWith("Keychain Data: ")) {
+ password = list[i].split(": ")[1];
+ break;
+ }
+ }
+ Display.FRAME.getContentPane().add(new JLabel("Found Screen Time passcode! Passcode: " + password));
+ System.out.println("Found Screen Time passcode! Passcode: " + password);
+ Display.refresh();
+ JButton button = new JButton("Back");
+ button.addActionListener(new BackListener());
+ Display.FRAME.getContentPane().add(button);
+ Display.refresh();
+ JOptionPane.showMessageDialog(null, "Found Screen Time passcode! Passcode: " + password);
+ }
+ } catch (Exception ex) {
+ handleException(ex, true);
+ Display.FRAME.getContentPane().add(new JLabel("Failed to retrieve Screen Time passcode! If you're sure you've done everything correctly, create an issue on GitHub."));
+ JButton button = new JButton("Back");
+ button.addActionListener(new BackListener());
+ Display.FRAME.getContentPane().add(button);
+ Display.refresh();
+ }
+ }
+ }.start();
+ }
+ }
public class ItunesBackupListener implements ActionListener {
public void actionPerformed(ActionEvent ev) {
try {
@@ -128,6 +237,14 @@ public static void refresh() {
Display.FRAME.revalidate();
Display.FRAME.repaint();
}
+ public static void refreshThread() {
+ new Thread() {
+ @Override
+ public void run() {
+ Display.refresh();
+ }
+ }.start();
+ }
public static class BackListener implements ActionListener {
public void actionPerformed(ActionEvent ev) {
Display.FRAME.getContentPane().removeAll();
diff --git a/src/com/emeryferrari/iosrr/RRConst.java b/src/com/emeryferrari/iosrr/RRConst.java
index 0e6fc6b..619c254 100644
--- a/src/com/emeryferrari/iosrr/RRConst.java
+++ b/src/com/emeryferrari/iosrr/RRConst.java
@@ -2,14 +2,15 @@
public class RRConst {
private RRConst() {}
public static final String NAME = "iOS-Restrictions-Recovery";
- public static final String VERSION = "v0.6.4";
+ public static final String VERSION = "v0.7";
public static final String TITLE = "
" + NAME + " " + VERSION + "";
- public static final String DESC = "Compatible only with iOS 7.0 through iOS 11.4.1";
+ public static final String DESC = "Compatible only with iOS 7.0 through iOS 13.x";
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 SSH_BUTTON = "From device via SSH (iOS 7.0 through iOS 11.4.1)";
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)";
+ public static final String KEYCHAIN_DUMPER = "From device via SSH (iOS 12.0 through iOS 13.x)";
}
\ No newline at end of file
diff --git a/src/com/emeryferrari/iosrr/RestrictionsRecovery.java b/src/com/emeryferrari/iosrr/RestrictionsRecovery.java
index e8b2bb2..bde3d9a 100644
--- a/src/com/emeryferrari/iosrr/RestrictionsRecovery.java
+++ b/src/com/emeryferrari/iosrr/RestrictionsRecovery.java
@@ -69,7 +69,6 @@ public static void downloadViaSSH(String ip, int port, String password, boolean
if (RestrictionsRecovery.identifyHostOS() != OperatingSystem.OTHER) {
SSHClient ssh = new SSHClient();
ssh.addHostKeyVerifier(new PromiscuousVerifier());
- ssh.loadKnownHosts();
ssh.connect(ip, port);
ssh.authPassword("root", password);
ssh.newSCPFileTransfer().download("/private/var/mobile/Library/Preferences/com.apple.restrictionspassword.plist", "password.plist");