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.maven2Nature org.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 + 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");