Compare commits

...

10 Commits
master ... 3.x

Author SHA1 Message Date
AloneMonkey cf05061131 [bugfix] dlopen parameter problem. 2019-07-24 10:57:24 +08:00
AloneMonkey 81b42b7007 [bugfix] fix for frida 12.5 2019-05-09 19:51:04 +08:00
AloneMonkey e3f6245905 [feature] auto load all frameworks and dylibs. 2019-05-02 23:20:52 +08:00
Alone_Monkey d23ef04f58
change frida to frida-tools 2018-11-10 17:18:48 +08:00
AloneMonkey ba054b9bc1 Exit when something goes wrong 2018-10-11 22:03:30 +08:00
AloneMonkey 89021072c2 fix bug 2018-07-14 19:24:08 +08:00
Alone_Monkey b48276f391
Compatible with previous versions 2018-07-13 16:42:28 +08:00
Alone_Monkey 7a6b65c040
change type tether to usb 2018-07-13 09:27:37 +08:00
AloneMonkey 094ada1051 [fix] search python3 fron env 2018-03-31 10:37:21 +08:00
AloneMonkey 4598333a18 [fix] add python 3.x version 2018-03-19 11:04:02 +08:00
4 changed files with 112 additions and 51 deletions

View File

@ -5,8 +5,9 @@ Pull a decrypted IPA from a jailbroken device
1. Install [frida](http://www.frida.re/) on device
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`
4. Run ./dump.py `Display name` or `Bundle identifier`
3. `sudo pip3 install -r requirements.txt --upgrade` (Python 3.x)
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.

85
dump.js
View File

@ -129,14 +129,15 @@ function getExportFunction(type, name, ret, args) {
}
}
NSSearchPathForDirectoriesInDomains = getExportFunction("f", "NSSearchPathForDirectoriesInDomains", "pointer", ["int", "int", "int"]);
wrapper_open = getExportFunction("f", "open", "int", ["pointer", "int", "int"]);
read = getExportFunction("f", "read", "int", ["int", "pointer", "int"]);
write = getExportFunction("f", "write", "int", ["int", "pointer", "int"]);
lseek = getExportFunction("f", "lseek", "int64", ["int", "int64", "int"]);
close = getExportFunction("f", "close", "int", ["int"]);
remove = getExportFunction("f", "remove", "int", ["pointer"]);
access = getExportFunction("f", "access", "int", ["pointer", "int"]);
var NSSearchPathForDirectoriesInDomains = getExportFunction("f", "NSSearchPathForDirectoriesInDomains", "pointer", ["int", "int", "int"]);
var wrapper_open = getExportFunction("f", "open", "int", ["pointer", "int", "int"]);
var read = getExportFunction("f", "read", "int", ["int", "pointer", "int"]);
var write = getExportFunction("f", "write", "int", ["int", "pointer", "int"]);
var lseek = getExportFunction("f", "lseek", "int64", ["int", "int64", "int"]);
var close = getExportFunction("f", "close", "int", ["int"]);
var remove = getExportFunction("f", "remove", "int", ["pointer"]);
var access = getExportFunction("f", "access", "int", ["pointer", "int"]);
var dlopen = getExportFunction("f", "dlopen", "pointer", ["pointer", "int"]);
function getDocumentDir() {
var NSDocumentDirectory = 9;
@ -154,7 +155,6 @@ function open(pathname, flags, mode) {
var modules = null;
function getAllAppModules() {
if (modules == null) {
modules = new Array();
var tmpmods = Process.enumerateModulesSync();
for (var i = 0; i < tmpmods.length; i++) {
@ -162,7 +162,6 @@ function getAllAppModules() {
modules.push(tmpmods[i]);
}
}
}
return modules;
}
@ -314,15 +313,75 @@ function dumpModule(name) {
return newmodpath
}
function loadAllDynamicLibrary(app_path) {
var defaultManager = ObjC.classes.NSFileManager.defaultManager();
var errorPtr = Memory.alloc(Process.pointerSize);
Memory.writePointer(errorPtr, NULL);
var filenames = defaultManager.contentsOfDirectoryAtPath_error_(app_path, errorPtr);
for (var i = 0, l = filenames.count(); i < l; i++) {
var file_name = filenames.objectAtIndex_(i);
var file_path = app_path.stringByAppendingPathComponent_(file_name);
if (file_name.hasSuffix_(".framework")) {
var bundle = ObjC.classes.NSBundle.bundleWithPath_(file_path);
if (bundle.isLoaded()) {
console.log("[frida-ios-dump]: " + file_name + " has been loaded. ");
} else {
if (bundle.load()) {
console.log("[frida-ios-dump]: Load " + file_name + " success. ");
} else {
console.log("[frida-ios-dump]: Load " + file_name + " failed. ");
}
}
} else if (file_name.hasSuffix_(".bundle") ||
file_name.hasSuffix_(".momd") ||
file_name.hasSuffix_(".strings") ||
file_name.hasSuffix_(".appex") ||
file_name.hasSuffix_(".app") ||
file_name.hasSuffix_(".lproj") ||
file_name.hasSuffix_(".storyboardc")) {
continue;
} else {
var isDirPtr = Memory.alloc(Process.pointerSize);
Memory.writePointer(isDirPtr,NULL);
defaultManager.fileExistsAtPath_isDirectory_(file_path, isDirPtr);
if (Memory.readPointer(isDirPtr) == 1) {
loadAllDynamicLibrary(file_path);
} else {
if (file_name.hasSuffix_(".dylib")) {
var is_loaded = 0;
for (var j = 0; j < modules.length; j++) {
if (modules[j].path.indexOf(file_name) != -1) {
is_loaded = 1;
console.log("[frida-ios-dump]: " + file_name + " has been dlopen.");
break;
}
}
if (!is_loaded) {
if (dlopen(allocStr(file_path.UTF8String()), 9)) {
console.log("[frida-ios-dump]: dlopen " + file_name + " success. ");
} else {
console.log("[frida-ios-dump]: dlopen " + file_name + " failed. ");
}
}
}
}
}
}
}
function handleMessage(message) {
//start dump
modules = getAllAppModules();
var app_path = ObjC.classes.NSBundle.mainBundle().bundlePath();
loadAllDynamicLibrary(app_path);
// start dump
modules = getAllAppModules();
for (var i = 0; i < modules.length; i++) {
console.log("start dump " + modules[i].path);
result = dumpModule(modules[i].path);
var result = dumpModule(modules[i].path);
send({ dump: result, path: modules[i].path});
}
send({app: ObjC.classes.NSBundle.mainBundle().bundlePath().toString()});
send({app: app_path.toString()});
send({done: "ok"});
recv(handleMessage);
}

59
dump.py
View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author : AloneMonkey
@ -22,9 +22,6 @@ from scp import SCPClient
from tqdm import tqdm
import traceback
reload(sys)
sys.setdefaultencoding('utf8')
script_dir = os.path.dirname(os.path.realpath(__file__))
DUMP_JS = os.path.join(script_dir, 'dump.js')
@ -43,6 +40,9 @@ finished = threading.Event()
def get_usb_iphone():
Type = 'usb'
if int(frida.__version__.split('.')[0]) < 12:
Type = 'tether'
device_manager = frida.get_device_manager()
changed = threading.Event()
@ -53,9 +53,9 @@ def get_usb_iphone():
device = None
while device is None:
devices = [dev for dev in device_manager.enumerate_devices() if dev.type == 'tether']
devices = [dev for dev in device_manager.enumerate_devices() if dev.type == Type]
if len(devices) == 0:
print 'Waiting for USB device...'
print('Waiting for USB device...')
changed.wait()
else:
device = devices[0]
@ -68,7 +68,7 @@ def get_usb_iphone():
def generate_ipa(path, display_name):
ipa_filename = display_name + '.ipa'
print 'Generating "{}"'.format(ipa_filename)
print('Generating "{}"'.format(ipa_filename))
try:
app_name = file_dict['app']
@ -82,9 +82,8 @@ def generate_ipa(path, display_name):
zip_args = ('zip', '-qr', os.path.join(os.getcwd(), ipa_filename), target_dir)
subprocess.check_call(zip_args, cwd=TEMP_DIR)
shutil.rmtree(PAYLOAD_PATH)
print
except Exception as e:
print e
print(e)
finished.set()
def on_message(message, data):
@ -92,7 +91,7 @@ def on_message(message, data):
last_sent = [0]
def progress(filename, size, sent):
t.desc = os.path.basename(filename)
t.desc = os.path.basename(filename).decode("utf-8")
t.total = size
t.update(sent - last_sent[0])
last_sent[0] = 0 if size == sent else sent
@ -104,7 +103,7 @@ def on_message(message, data):
dump_path = payload['dump']
scp_from = dump_path
scp_to = PAYLOAD_PATH + u'/'
scp_to = PAYLOAD_PATH + '/'
with SCPClient(ssh.get_transport(), progress = progress, socket_timeout = 60) as scp:
scp.get(scp_from, scp_to)
@ -114,7 +113,7 @@ def on_message(message, data):
try:
subprocess.check_call(chmod_args)
except subprocess.CalledProcessError as err:
print err
print(err)
index = origin_path.find('.app/')
file_dict[os.path.basename(dump_path)] = origin_path[index + 5:]
@ -123,7 +122,7 @@ def on_message(message, data):
app_path = payload['app']
scp_from = app_path
scp_to = PAYLOAD_PATH + u'/'
scp_to = PAYLOAD_PATH + '/'
with SCPClient(ssh.get_transport(), progress = progress, socket_timeout = 60) as scp:
scp.get(scp_from, scp_to, recursive=True)
@ -132,7 +131,7 @@ def on_message(message, data):
try:
subprocess.check_call(chmod_args)
except subprocess.CalledProcessError as err:
print err
print(err)
file_dict['app'] = os.path.basename(app_path)
@ -188,8 +187,7 @@ def get_applications(device):
try:
applications = device.enumerate_applications()
except Exception as e:
print 'Failed to enumerate applications: %s' % e
return
sys.exit('Failed to enumerate applications: %s' % e)
return applications
@ -208,15 +206,15 @@ def list_applications(device):
header_format = '%' + str(pid_column_width) + 's ' + '%-' + str(name_column_width) + 's ' + '%-' + str(
identifier_column_width) + 's'
print header_format % ('PID', 'Name', 'Identifier')
print '%s %s %s' % (pid_column_width * '-', name_column_width * '-', identifier_column_width * '-')
print(header_format % ('PID', 'Name', 'Identifier'))
print('%s %s %s' % (pid_column_width * '-', name_column_width * '-', identifier_column_width * '-'))
line_format = '%' + str(pid_column_width) + 's ' + '%-' + str(name_column_width) + 's ' + '%-' + str(
identifier_column_width) + 's'
for application in sorted(applications, key=cmp_to_key(compare_applications)):
if application.pid == 0:
print line_format % ('-', application.name, application.identifier)
print(line_format % ('-', application.name, application.identifier))
else:
print line_format % (application.pid, application.name, application.identifier)
print(line_format % (application.pid, application.name, application.identifier))
def load_js_file(session, filename):
@ -238,11 +236,11 @@ def create_dir(path):
try:
os.makedirs(path)
except os.error as err:
print err
print(err)
def open_target_app(device, name_or_bundleid):
print 'Start the target app {}'.format(name_or_bundleid)
print('Start the target app {}'.format(name_or_bundleid))
pid = ''
session = None
@ -262,13 +260,13 @@ def open_target_app(device, name_or_bundleid):
else:
session = device.attach(pid)
except Exception as e:
print e
print(e)
return session, display_name, bundle_identifier
def start_dump(session, ipa_name):
print 'Dumping {} to {}'.format(display_name, TEMP_DIR)
print('Dumping {} to {}'.format(display_name, TEMP_DIR))
script = load_js_file(session, DUMP_JS)
script.post('dump')
@ -289,11 +287,14 @@ if __name__ == '__main__':
exit_code = 0
ssh = None
if not len(sys.argv[1:]):
parser.print_help()
sys.exit(exit_code)
device = get_usb_iphone()
if len(sys.argv[1:]) == 0:
parser.print_help()
elif args.list_applications:
if args.list_applications:
list_applications(device)
else:
name_or_bundleid = args.target
@ -312,10 +313,10 @@ if __name__ == '__main__':
if session:
start_dump(session, output_ipa)
except paramiko.ssh_exception.NoValidConnectionsError as e:
print e
print(e)
exit_code = 1
except paramiko.AuthenticationException as e:
print e
print(e)
exit_code = 1
except Exception as e:
print('*** Caught exception: %s: %s' % (e.__class__, e))

View File

@ -4,7 +4,7 @@ cffi
colorama
cryptography
enum34
frida
frida-tools
idna
ipaddress
paramiko