Compare commits
10 Commits
Author | SHA1 | Date |
---|---|---|
|
cf05061131 | |
|
81b42b7007 | |
|
e3f6245905 | |
|
d23ef04f58 | |
|
ba054b9bc1 | |
|
89021072c2 | |
|
b48276f391 | |
|
7a6b65c040 | |
|
094ada1051 | |
|
4598333a18 |
13
README.md
13
README.md
|
@ -1,13 +1,13 @@
|
||||||
# frida-ios-dump
|
# frida-ios-dump
|
||||||
Pull a decrypted IPA from a jailbroken device
|
Pull a decrypted IPA from a jailbroken device
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
1. Install [frida](http://www.frida.re/) on device
|
1. Install [frida](http://www.frida.re/) on device
|
||||||
2. `sudo pip install -r requirements.txt --upgrade`
|
2. `sudo pip install -r requirements.txt --upgrade` (Python 2.7)
|
||||||
3. Run usbmuxd/iproxy SSH forwarding over USB (Default 2222 -> 22). e.g. `iproxy 2222 22`
|
3. `sudo pip3 install -r requirements.txt --upgrade` (Python 3.x)
|
||||||
4. Run ./dump.py `Display name` or `Bundle identifier`
|
4. Run usbmuxd/iproxy SSH forwarding over USB (Default 2222 -> 22). e.g. `iproxy 2222 22`
|
||||||
|
5. Run ./dump.py `Display name` or `Bundle identifier`
|
||||||
|
|
||||||
For SSH/SCP make sure you have your public key added to the target device's ~/.ssh/authorized_keys file.
|
For SSH/SCP make sure you have your public key added to the target device's ~/.ssh/authorized_keys file.
|
||||||
|
|
||||||
|
@ -40,11 +40,6 @@ Congratulations!!! You've got a decrypted IPA file.
|
||||||
|
|
||||||
Drag to [MonkeyDev](https://github.com/AloneMonkey/MonkeyDev), Happy hacking!
|
Drag to [MonkeyDev](https://github.com/AloneMonkey/MonkeyDev), Happy hacking!
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
Python 2.x and 3.x
|
|
||||||
|
|
||||||
|
|
||||||
### issues
|
### issues
|
||||||
|
|
||||||
If the following error occurs:
|
If the following error occurs:
|
||||||
|
|
67
dump.py
67
dump.py
|
@ -1,32 +1,27 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Author : AloneMonkey
|
# Author : AloneMonkey
|
||||||
# blog: www.alonemonkey.com
|
# blog: www.alonemonkey.com
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import sys
|
import sys
|
||||||
import codecs
|
import codecs
|
||||||
import frida
|
import frida
|
||||||
import threading
|
import threading
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import time
|
import time
|
||||||
import argparse
|
import argparse
|
||||||
import tempfile
|
import tempfile
|
||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import paramiko
|
import paramiko
|
||||||
from paramiko import SSHClient
|
from paramiko import SSHClient
|
||||||
from scp import SCPClient
|
from scp import SCPClient
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
IS_PY2 = sys.version_info[0] < 3
|
|
||||||
if IS_PY2:
|
|
||||||
reload(sys)
|
|
||||||
sys.setdefaultencoding('utf8')
|
|
||||||
|
|
||||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
DUMP_JS = os.path.join(script_dir, 'dump.js')
|
DUMP_JS = os.path.join(script_dir, 'dump.js')
|
||||||
|
@ -35,7 +30,6 @@ User = 'root'
|
||||||
Password = 'alpine'
|
Password = 'alpine'
|
||||||
Host = 'localhost'
|
Host = 'localhost'
|
||||||
Port = 2222
|
Port = 2222
|
||||||
KeyFileName = None
|
|
||||||
|
|
||||||
TEMP_DIR = tempfile.gettempdir()
|
TEMP_DIR = tempfile.gettempdir()
|
||||||
PAYLOAD_DIR = 'Payload'
|
PAYLOAD_DIR = 'Payload'
|
||||||
|
@ -77,22 +71,19 @@ def generate_ipa(path, display_name):
|
||||||
print('Generating "{}"'.format(ipa_filename))
|
print('Generating "{}"'.format(ipa_filename))
|
||||||
try:
|
try:
|
||||||
app_name = file_dict['app']
|
app_name = file_dict['app']
|
||||||
|
|
||||||
for key, value in file_dict.items():
|
for key, value in file_dict.items():
|
||||||
from_dir = os.path.join(path, key)
|
from_dir = os.path.join(path, key)
|
||||||
to_dir = os.path.join(path, app_name, value)
|
to_dir = os.path.join(path, app_name, value)
|
||||||
if key != 'app':
|
if key != 'app':
|
||||||
try:
|
shutil.move(from_dir, to_dir)
|
||||||
os.rename(from_dir, to_dir)
|
|
||||||
except FileExistsError:
|
|
||||||
os.remove(to_dir)
|
|
||||||
os.rename(from_dir, to_dir)
|
|
||||||
target_dir = './' + PAYLOAD_DIR
|
target_dir = './' + PAYLOAD_DIR
|
||||||
zip_args = ("7z", "a", "-r", f"{os.getcwd()}/{ipa_filename}.zip", "-w", f"{target_dir}", "-mem=AES256")
|
zip_args = ('zip', '-qr', os.path.join(os.getcwd(), ipa_filename), target_dir)
|
||||||
subprocess.check_call(zip_args, cwd=TEMP_DIR)
|
subprocess.check_call(zip_args, cwd=TEMP_DIR)
|
||||||
os.rename(f"{os.getcwd()}/{ipa_filename}.zip", f"{os.getcwd()}/{ipa_filename}")
|
shutil.rmtree(PAYLOAD_PATH)
|
||||||
os.system('rmdir /S /Q "{}"'.format(PAYLOAD_PATH))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"{type(e)}: {e}")
|
print(e)
|
||||||
finished.set()
|
finished.set()
|
||||||
|
|
||||||
def on_message(message, data):
|
def on_message(message, data):
|
||||||
|
@ -100,11 +91,7 @@ def on_message(message, data):
|
||||||
last_sent = [0]
|
last_sent = [0]
|
||||||
|
|
||||||
def progress(filename, size, sent):
|
def progress(filename, size, sent):
|
||||||
baseName = os.path.basename(filename)
|
t.desc = os.path.basename(filename).decode("utf-8")
|
||||||
if IS_PY2 or isinstance(baseName, bytes):
|
|
||||||
t.desc = baseName.decode("utf-8")
|
|
||||||
else:
|
|
||||||
t.desc = baseName
|
|
||||||
t.total = size
|
t.total = size
|
||||||
t.update(sent - last_sent[0])
|
t.update(sent - last_sent[0])
|
||||||
last_sent[0] = 0 if size == sent else sent
|
last_sent[0] = 0 if size == sent else sent
|
||||||
|
@ -123,12 +110,10 @@ def on_message(message, data):
|
||||||
|
|
||||||
chmod_dir = os.path.join(PAYLOAD_PATH, os.path.basename(dump_path))
|
chmod_dir = os.path.join(PAYLOAD_PATH, os.path.basename(dump_path))
|
||||||
chmod_args = ('chmod', '655', chmod_dir)
|
chmod_args = ('chmod', '655', chmod_dir)
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(chmod_args)
|
subprocess.check_call(chmod_args)
|
||||||
except subprocess.CalledProcessError as err:
|
except subprocess.CalledProcessError as err:
|
||||||
print(err)
|
print(err)
|
||||||
"""
|
|
||||||
|
|
||||||
index = origin_path.find('.app/')
|
index = origin_path.find('.app/')
|
||||||
file_dict[os.path.basename(dump_path)] = origin_path[index + 5:]
|
file_dict[os.path.basename(dump_path)] = origin_path[index + 5:]
|
||||||
|
@ -143,12 +128,11 @@ def on_message(message, data):
|
||||||
|
|
||||||
chmod_dir = os.path.join(PAYLOAD_PATH, os.path.basename(app_path))
|
chmod_dir = os.path.join(PAYLOAD_PATH, os.path.basename(app_path))
|
||||||
chmod_args = ('chmod', '755', chmod_dir)
|
chmod_args = ('chmod', '755', chmod_dir)
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(chmod_args)
|
subprocess.check_call(chmod_args)
|
||||||
except subprocess.CalledProcessError as err:
|
except subprocess.CalledProcessError as err:
|
||||||
print(err)
|
print(err)
|
||||||
"""
|
|
||||||
file_dict['app'] = os.path.basename(app_path)
|
file_dict['app'] = os.path.basename(app_path)
|
||||||
|
|
||||||
if 'done' in payload:
|
if 'done' in payload:
|
||||||
|
@ -248,7 +232,7 @@ def create_dir(path):
|
||||||
path = path.strip()
|
path = path.strip()
|
||||||
path = path.rstrip('\\')
|
path = path.rstrip('\\')
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
os.system('rmdir /S /Q "{}"'.format(path))
|
shutil.rmtree(path)
|
||||||
try:
|
try:
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
except os.error as err:
|
except os.error as err:
|
||||||
|
@ -298,13 +282,7 @@ if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser(description='frida-ios-dump (by AloneMonkey v2.0)')
|
parser = argparse.ArgumentParser(description='frida-ios-dump (by AloneMonkey v2.0)')
|
||||||
parser.add_argument('-l', '--list', dest='list_applications', action='store_true', help='List the installed apps')
|
parser.add_argument('-l', '--list', dest='list_applications', action='store_true', help='List the installed apps')
|
||||||
parser.add_argument('-o', '--output', dest='output_ipa', help='Specify name of the decrypted IPA')
|
parser.add_argument('-o', '--output', dest='output_ipa', help='Specify name of the decrypted IPA')
|
||||||
parser.add_argument('-H', '--host', dest='ssh_host', help='Specify SSH hostname')
|
|
||||||
parser.add_argument('-p', '--port', dest='ssh_port', help='Specify SSH port')
|
|
||||||
parser.add_argument('-u', '--user', dest='ssh_user', help='Specify SSH username')
|
|
||||||
parser.add_argument('-P', '--password', dest='ssh_password', help='Specify SSH password')
|
|
||||||
parser.add_argument('-K', '--key_filename', dest='ssh_key_filename', help='Specify SSH private key file path')
|
|
||||||
parser.add_argument('target', nargs='?', help='Bundle identifier or display name of the target app')
|
parser.add_argument('target', nargs='?', help='Bundle identifier or display name of the target app')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
exit_code = 0
|
exit_code = 0
|
||||||
|
@ -321,22 +299,11 @@ if __name__ == '__main__':
|
||||||
else:
|
else:
|
||||||
name_or_bundleid = args.target
|
name_or_bundleid = args.target
|
||||||
output_ipa = args.output_ipa
|
output_ipa = args.output_ipa
|
||||||
# update ssh args
|
|
||||||
if args.ssh_host:
|
|
||||||
Host = args.ssh_host
|
|
||||||
if args.ssh_port:
|
|
||||||
Port = int(args.ssh_port)
|
|
||||||
if args.ssh_user:
|
|
||||||
User = args.ssh_user
|
|
||||||
if args.ssh_password:
|
|
||||||
Password = args.ssh_password
|
|
||||||
if args.ssh_key_filename:
|
|
||||||
KeyFileName = args.ssh_key_filename
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ssh = paramiko.SSHClient()
|
ssh = paramiko.SSHClient()
|
||||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
ssh.connect(Host, port=Port, username=User, password=Password, key_filename=KeyFileName)
|
ssh.connect(Host, port=Port, username=User, password=Password)
|
||||||
|
|
||||||
create_dir(PAYLOAD_PATH)
|
create_dir(PAYLOAD_PATH)
|
||||||
(session, display_name, bundle_identifier) = open_target_app(device, name_or_bundleid)
|
(session, display_name, bundle_identifier) = open_target_app(device, name_or_bundleid)
|
||||||
|
@ -346,12 +313,10 @@ if __name__ == '__main__':
|
||||||
if session:
|
if session:
|
||||||
start_dump(session, output_ipa)
|
start_dump(session, output_ipa)
|
||||||
except paramiko.ssh_exception.NoValidConnectionsError as e:
|
except paramiko.ssh_exception.NoValidConnectionsError as e:
|
||||||
print(e)
|
print(e)
|
||||||
print('Try specifying -H/--hostname and/or -p/--port')
|
|
||||||
exit_code = 1
|
exit_code = 1
|
||||||
except paramiko.AuthenticationException as e:
|
except paramiko.AuthenticationException as e:
|
||||||
print(e)
|
print(e)
|
||||||
print('Try specifying -u/--username and/or -P/--password')
|
|
||||||
exit_code = 1
|
exit_code = 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('*** Caught exception: %s: %s' % (e.__class__, e))
|
print('*** Caught exception: %s: %s' % (e.__class__, e))
|
||||||
|
@ -362,6 +327,6 @@ if __name__ == '__main__':
|
||||||
ssh.close()
|
ssh.close()
|
||||||
|
|
||||||
if os.path.exists(PAYLOAD_PATH):
|
if os.path.exists(PAYLOAD_PATH):
|
||||||
os.system('rmdir /S /Q "{}"'.format(PAYLOAD_PATH))
|
shutil.rmtree(PAYLOAD_PATH)
|
||||||
|
|
||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
|
|
Loading…
Reference in New Issue