Merge pull request #4 from palmerc/master

Updates you might be interested in
This commit is contained in:
Alone_Monkey 2018-02-07 19:31:50 +08:00 committed by GitHub
commit 6abc0cf79c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 363 additions and 319 deletions

4
.gitignore vendored
View File

@ -1 +1,5 @@
.DS_Store
.idea
*.ipa
frida/

View File

@ -1,50 +1,39 @@
# frida-ios-dump
pull decrypted ipa from jailbreak device
Pull a decrypted IPA from a jailbroken device
### Usage
## Usage
## 1. install [frida](http://www.frida.re/) on device and mac
1. Install [frida](http://www.frida.re/) on device and mac
2. Run usbmuxd/iproxy SSH forwarding over USB (Default 2222 -> 22)
3. Run ./dump.py `Display name` or `Bundle identifier`
## 2. iproxy 2222 22
## 3. ./dump.py 微信
For SSH/SCP make sure you have your public key added to the target device's ~/.ssh/authorized_keys file.
```
➜ frida-ios-dump ./dump.py 微信
open target app......
start dump target app......
start dump /var/containers/Bundle/Application/6665AA28-68CC-4845-8610-7010E96061C6/WeChat.app/WeChat
WeChat 100% 68MB 11.4MB/s 00:05
start dump /private/var/containers/Bundle/Application/6665AA28-68CC-4845-8610-7010E96061C6/WeChat.app/Frameworks/WCDB.framework/WCDB
WCDB 100% 2555KB 11.0MB/s 00:00
start dump /private/var/containers/Bundle/Application/6665AA28-68CC-4845-8610-7010E96061C6/WeChat.app/Frameworks/MMCommon.framework/MMCommon
MMCommon 100% 979KB 10.6MB/s 00:00
start dump /private/var/containers/Bundle/Application/6665AA28-68CC-4845-8610-7010E96061C6/WeChat.app/Frameworks/MultiMedia.framework/MultiMedia
MultiMedia 100% 6801KB 11.1MB/s 00:00
start dump /private/var/containers/Bundle/Application/6665AA28-68CC-4845-8610-7010E96061C6/WeChat.app/Frameworks/mars.framework/mars
mars 100% 7462KB 11.1MB/s 00:00
AppIcon60x60@2x.png 100% 2253 230.9KB/s 00:00
AppIcon60x60@3x.png 100% 4334 834.8KB/s 00:00
AppIcon76x76@2x~ipad.png 100% 2659 620.6KB/s 00:00
AppIcon76x76~ipad.png 100% 1523 358.0KB/s 00:00
AppIcon83.5x83.5@2x~ipad.png 100% 2725 568.9KB/s 00:00
Assets.car 100% 10MB 11.1MB/s 00:00
.......
AppIntentVocabulary.plist 100% 197 52.9KB/s 00:00
AppIntentVocabulary.plist 100% 167 43.9KB/s 00:00
AppIntentVocabulary.plist 100% 187 50.2KB/s 00:00
InfoPlist.strings 100% 1720 416.4KB/s 00:00
TipsPressTalk@2x.png 100% 14KB 2.2MB/s 00:00
mm.strings 100% 404KB 10.2MB/s 00:00
network_setting.html 100% 1695 450.4KB/s 00:00
InfoPlist.strings 100% 1822 454.1KB/s 00:00
mm.strings 100% 409KB 10.2MB/s 00:00
network_setting.html 100% 1819 477.5KB/s 00:00
InfoPlist.strings 100% 1814 466.8KB/s 00:00
mm.strings 100% 409KB 10.3MB/s 00:00
network_setting.html 100% 1819 404.9KB/s 00:00
./dump.py Aftenposten
Start the target app Aftenposten
Dumping Aftenposten to /var/folders/wn/9v1hs8ds6nv_xj7g95zxyl140000gn/T
start dump /var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/AftenpostenApp
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/AFNetworking.framework/AFNetworking
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/ATInternet_iOS_ObjC_SDK.framework/ATInternet_iOS_ObjC_SDK
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/SPTEventCollector.framework/SPTEventCollector
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/SPiDSDK.framework/SPiDSDK
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/libswiftCore.dylib
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/libswiftCoreData.dylib
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/libswiftCoreGraphics.dylib
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/libswiftCoreImage.dylib
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/libswiftCoreLocation.dylib
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/libswiftDarwin.dylib
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/libswiftDispatch.dylib
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/libswiftFoundation.dylib
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/libswiftObjectiveC.dylib
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/libswiftQuartzCore.dylib
start dump /private/var/containers/Bundle/Application/66423A80-0AFE-471C-BC9B-B571107D3C27/AftenpostenApp.app/Frameworks/libswiftUIKit.dylib
Generating Aftenposten.ipa
Done.
```
congratulations!!! You've got a decrypted ipa file.
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!

52
app.js
View File

@ -1,52 +0,0 @@
//by: AloneMonkey
const LSApplicationWorkspace = ObjC.classes.LSApplicationWorkspace;
function openApplication(appid){
const workspace = LSApplicationWorkspace.defaultWorkspace();
return workspace.openApplicationWithBundleID_(appid);
}
function getbundleid(name){
const workspace = LSApplicationWorkspace.defaultWorkspace();
const apps = workspace.allApplications();
var result;
for(var index = 0; index < apps.count(); index++){
var proxy = apps.objectAtIndex_(index);
if(proxy.localizedName() && proxy.localizedName().toString() == name){
return proxy.bundleIdentifier().toString();
}
}
return ""
};
function getdisplayname(bundleid){
const workspace = LSApplicationWorkspace.defaultWorkspace();
const apps = workspace.allApplications();
var result;
for(var index = 0; index < apps.count(); index++){
var proxy = apps.objectAtIndex_(index);
if(proxy.bundleIdentifier() && proxy.bundleIdentifier().toString() == bundleid){
return proxy.localizedName().toString();
}
}
return ""
}
function handleMessage(message) {
var bundleid;
var displayname;
if(message['name']){
displayname = message['name']
bundleid = getbundleid(displayname);
}else if(message['bundleid']){
bundleid = message['bundleid']
displayname = getdisplayname(bundleid);
}
if(bundleid.length > 0){
openApplication(bundleid);
}
send({ opened: displayname });
}
recv(handleMessage);

14
dump.js
View File

@ -175,9 +175,9 @@ var LC_SEGMENT_64 = 0x19;
var LC_ENCRYPTION_INFO = 0x21;
var LC_ENCRYPTION_INFO_64 = 0x2C;
function pad(str, n) {
return Array(n-str.length+1).join("0")+str;
}
function pad(str, n) {
return Array(n-str.length+1).join("0")+str;
}
function swap32(value) {
value = pad(value.toString(16),8)
@ -214,7 +214,7 @@ function dumpModule(name) {
if(!access(allocStr(newmodpath),0)){
remove(allocStr(newmodpath));
}
var fmodule = open(newmodpath, O_CREAT | O_RDWR, 0);
var foldmodule = open(oldmodpath, O_RDONLY, 0);
@ -288,7 +288,7 @@ function dumpModule(name) {
var segments = [];
for (var i = 0; i < ncmds; i++) {
var cmd = getU32(modbase.add(off));
var cmdsize = getU32(modbase.add(off + 4));
var cmdsize = getU32(modbase.add(off + 4));
if (cmd == LC_ENCRYPTION_INFO || cmd == LC_ENCRYPTION_INFO_64) {
offset_cryptid = off + 16;
crypt_off = getU32(modbase.add(off + 8));
@ -305,11 +305,11 @@ function dumpModule(name) {
lseek(fmodule, crypt_off, SEEK_SET);
write(fmodule, modbase.add(crypt_off), crypt_size);
}
close(fmodule);
close(foldmodule);
return newmodpath
}
}
function handleMessage(message) {
//start dump

483
dump.py
View File

@ -1,272 +1,317 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Author : AloneMonkey
#blog: www.alonemonkey.com
# Author : AloneMonkey
# blog: www.alonemonkey.com
import sys
import codecs
import frida
import threading
import threading
import os
import shutil
import time
import getopt
import argparse
import tempfile
import subprocess
import re
import paramiko
from paramiko import SSHClient
from scp import SCPClient
import traceback
reload(sys)
sys.setdefaultencoding('utf8')
DUMP_JS = './dump.js'
APP_JS = './app.js'
OUTPUT = "Payload"
script_dir = os.path.dirname(os.path.realpath(__file__))
DUMP_JS = os.path.join(script_dir, 'dump.js')
User = 'root'
Password = 'alpine'
Host = 'localhost'
Port = 2222
TEMP_DIR = tempfile.gettempdir()
PAYLOAD_DIR = 'Payload'
PAYLOAD_PATH = os.path.join(TEMP_DIR, PAYLOAD_DIR)
file_dict = {}
opened = threading.Event()
finished = threading.Event()
global session
global name
#show usage
def usage():
print '-------------------------frida-ios-dump(by AloneMonkey v2.0)----------------------------'
print '\t%-20s\t%s' % ('-h,--help','Show help menu.');
print '\t%-20s\t%s' % ('displayname','Decrypt the application of the specified display name. ps: ./dump.py 微信');
print '\t%-20s\t%s' % ('-l','List the app has been installed.');
print '\t%-20s\t%s' % ('-b bundleid','Decrypt the application of the specified bundleid. ps: ./dump.py com.tencent.xin');
exit(0)
def get_usb_iphone():
dManager = frida.get_device_manager();
changed = threading.Event()
def on_changed():
changed.set()
dManager.on('changed',on_changed)
device_manager = frida.get_device_manager()
changed = threading.Event()
device = None
while device is None:
devices = [dev for dev in dManager.enumerate_devices() if dev.type == 'tether']
if len(devices) == 0:
print 'Waiting for usb device...'
changed.wait()
else:
device = devices[0]
def on_changed():
changed.set()
dManager.off('changed',on_changed)
return device
device_manager.on('changed', on_changed)
def gen_ipa(target):
try:
app_name = file_dict["app"]
for key, value in file_dict.items():
if key != "app":
shutil.move(target +"/"+ key, target + "/" + app_name + "/" + value);
(shotname,extension) = os.path.splitext(app_name)
os.system(u''.join(("zip -qr ", name.replace(" ", "\\ "), ".ipa ./Payload")).encode('utf-8').strip());
os.system("rm -rf ./Payload");
except Exception as e:
print e
finished.set();
device = None
while device is None:
devices = [dev for dev in device_manager.enumerate_devices() if dev.type == 'tether']
if len(devices) == 0:
print 'Waiting for USB device...'
changed.wait()
else:
device = devices[0]
device_manager.off('changed', on_changed)
return device
def generate_ipa(path, display_name):
ipa_filename = display_name + '.ipa'
print 'Generating "{}"'.format(ipa_filename)
try:
app_name = file_dict['app']
for key, value in file_dict.items():
from_dir = os.path.join(path, key)
to_dir = os.path.join(path, app_name, value)
if key != 'app':
shutil.move(from_dir, to_dir)
target_dir = './' + PAYLOAD_DIR
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
finished.set()
def on_message(message, data):
global name
if 'payload' in message:
payload = message['payload']
if 'opened' in payload:
name = payload['opened']
if 'dump' in payload:
origin_path = payload['path']
dump_path = payload['dump']
scp_from = dump_path
scp_to = PAYLOAD_PATH + u'/'
with SCPClient(ssh.get_transport()) as scp:
scp.get(scp_from, scp_to)
chmod_dir = os.path.join(PAYLOAD_PATH, os.path.basename(dump_path))
chmod_args = ('chmod', '655', chmod_dir)
try:
subprocess.check_call(chmod_args)
except subprocess.CalledProcessError as err:
print err
index = origin_path.find('.app/')
file_dict[os.path.basename(dump_path)] = origin_path[index + 5:]
if 'app' in payload:
app_path = payload['app']
scp_from = app_path
scp_to = PAYLOAD_PATH + u'/'
with SCPClient(ssh.get_transport()) as scp:
scp.get(scp_from, scp_to, recursive=True)
chmod_dir = os.path.join(PAYLOAD_PATH, os.path.basename(app_path))
chmod_args = ('chmod', '755', chmod_dir)
try:
subprocess.check_call(chmod_args)
except subprocess.CalledProcessError as err:
print err
file_dict['app'] = os.path.basename(app_path)
if 'done' in payload:
finished.set()
def on_message(message,data):
global name;
if message.has_key('payload'):
payload = message['payload']
if payload.has_key("opened"):
name = payload["opened"]
opened.set();
if payload.has_key("dump"):
orign_path = payload["path"]
dumppath = payload["dump"]
os.system(u''.join(("scp -P 2222 root@localhost:", dumppath.replace(" ", "\\\\\ "), u" ./" + OUTPUT + u"/")).encode('utf-8').strip())
os.system(u''.join(("chmod 655 ", u'./' + OUTPUT + u'/', os.path.basename(dumppath.replace(" ", "\\ ")))).encode('utf-8').strip())
index = orign_path.find(".app/")
file_dict[os.path.basename(dumppath)] = orign_path[index+5:]
if payload.has_key("app"):
apppath = payload["app"]
os.system(u''.join(("scp -r -P 2222 root@localhost:", apppath.replace(" ", "\\\\\ "), u" ./" + OUTPUT + u"/")).encode('utf-8').strip())
os.system(u''.join(("chmod 755 ", u'./' + OUTPUT + u'/', os.path.basename(apppath.replace(" ", "\\ ")))).encode('utf-8').strip())
file_dict["app"] = os.path.basename(apppath)
if payload.has_key("done"):
gen_ipa(os.getcwd()+"/"+OUTPUT)
finished.set();
def compare_applications(a, b):
a_is_running = a.pid != 0
b_is_running = b.pid != 0
if a_is_running == b_is_running:
if a.name > b.name:
return 1
elif a.name < b.name:
return -1
else:
return 0
elif a_is_running:
a_is_running = a.pid != 0
b_is_running = b.pid != 0
if a_is_running == b_is_running:
if a.name > b.name:
return 1
elif a.name < b.name:
return -1
else:
return 1
return 0
elif a_is_running:
return -1
else:
return 1
def cmp_to_key(mycmp):
"Convert a cmp= function into a key= function"
class K:
def __init__(self, obj, *args):
self.obj = obj
def __lt__(self, other):
return mycmp(self.obj, other.obj) < 0
def __gt__(self, other):
return mycmp(self.obj, other.obj) > 0
def __eq__(self, other):
return mycmp(self.obj, other.obj) == 0
def __le__(self, other):
return mycmp(self.obj, other.obj) <= 0
def __ge__(self, other):
return mycmp(self.obj, other.obj) >= 0
def __ne__(self, other):
return mycmp(self.obj, other.obj) != 0
return K
"""Convert a cmp= function into a key= function"""
def get_applications():
device = get_usb_iphone()
class K:
def __init__(self, obj):
self.obj = obj
try:
applications = device.enumerate_applications()
except Exception as e:
print "Failed to enumerate applications: %s" % e
exit(1)
return
def __lt__(self, other):
return mycmp(self.obj, other.obj) < 0
return applications
def __gt__(self, other):
return mycmp(self.obj, other.obj) > 0
def list_applications():
applications = get_applications()
def __eq__(self, other):
return mycmp(self.obj, other.obj) == 0
if len(applications) > 0:
pid_column_width = max(map(lambda app: len("%d" % app.pid), applications))
name_column_width = max(map(lambda app: len(app.name), applications))
identifier_column_width = max(map(lambda app: len(app.identifier), applications))
else:
pid_column_width = 0
name_column_width = 0
identifier_column_width = 0
def __le__(self, other):
return mycmp(self.obj, other.obj) <= 0
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 * "-")
line_format = "%" + str(pid_column_width) + "s " + "%-" + str(name_column_width) + "s " + "%-" + str(identifier_column_width) + "s"
for app in sorted(applications, key=cmp_to_key(compare_applications)):
if app.pid == 0:
print line_format % ("-", app.name, app.identifier)
else:
print line_format % (app.pid, app.name, app.identifier)
def __ge__(self, other):
return mycmp(self.obj, other.obj) >= 0
def get_pid_by_bundleid(bundleid):
applications = get_applications()
def __ne__(self, other):
return mycmp(self.obj, other.obj) != 0
return [app.pid for app in applications if app.identifier == bundleid][0]
def find_target_app(isbundleid, value):
applications = get_applications()
return K
def get_applications(device):
try:
applications = device.enumerate_applications()
except Exception as e:
print 'Failed to enumerate applications: %s' % e
return
return applications
def list_applications(device):
applications = get_applications(device)
if len(applications) > 0:
pid_column_width = max(map(lambda app: len('{}'.format(app.pid)), applications))
name_column_width = max(map(lambda app: len(app.name), applications))
identifier_column_width = max(map(lambda app: len(app.identifier), applications))
else:
pid_column_width = 0
name_column_width = 0
identifier_column_width = 0
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 * '-')
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)
else:
print line_format % (application.pid, application.name, application.identifier)
if not isbundleid:
return [app for app in applications if app.name == value]
else:
return [app for app in applications if app.identifier == value]
def load_js_file(session, filename):
source = ''
with codecs.open(filename,'r','utf-8') as f:
source = source + f.read();
script = session.create_script(source);
script.on("message",on_message)
script.load()
return script
source = ''
with codecs.open(filename, 'r', 'utf-8') as f:
source = source + f.read()
script = session.create_script(source)
script.on('message', on_message)
script.load()
return script
def clear_and_quit(session):
if session:
session.detach()
sys.exit(0)
def create_dir(path):
path = path.strip()
path = path.rstrip("\\")
if not os.path.exists(path):
os.makedirs(path)
else:
print path + u" is existed!";
path = path.strip()
path = path.rstrip('\\')
if os.path.exists(path):
shutil.rmtree(path)
try:
os.makedirs(path)
except os.error as err:
print err
def open_target_app(isbundleid, value):
global session;
device = get_usb_iphone();
name = u'SpringBoard';
print "open target app......"
session = device.attach(name);
script = load_js_file(session, APP_JS);
if not isbundleid:
script.post({'name': value})
else:
script.post({'bundleid': value})
opened.wait();
session.detach();
create_dir(os.getcwd()+"/"+OUTPUT)
print 'Waiting for the application to open......'
time.sleep(5);
def start_dump(target):
global session;
print "start dump target app......"
device = get_usb_iphone();
session = device.attach(target);
script = load_js_file(session, DUMP_JS);
script.post("dump");
finished.wait();
clear_and_quit(session);
def open_target_app(device, name_or_bundleid):
print 'Start the target app {}'.format(name_or_bundleid)
def dump_by_display_name(display_name):
open_target_app( 0, display_name)
start_dump(display_name);
pid = -1
display_name = ''
bundle_identifier = ''
for application in get_applications(device):
if name_or_bundleid == application.identifier or name_or_bundleid == application.name:
display_name = application.name
bundle_identifier = application.identifier
def dump_by_bundleid(bundleid):
open_target_app( 1, bundleid)
start_dump(get_pid_by_bundleid(bundleid));
try:
pid = device.spawn([bundle_identifier])
device.resume(pid)
time.sleep(1)
except Exception as e:
print e
def check_args():
if len(sys.argv) < 2:
usage()
sys.exit(1)
return pid, display_name, bundle_identifier
try:
opts,args = getopt.getopt(sys.argv[1:],"hlb:",["help"]);
except getopt.GetoptError:
usage()
sys.exit(2)
for opt,value in opts:
if opt in ("-h","--help"):
usage()
if opt in ("-l"):
list_applications()
if opt in ("-b"):
if not find_target_app(1, value):
print "can not find target app '%s'" % value
else:
dump_by_bundleid(value)
def start_dump(device, pid, ipa_name):
print 'Dumping {} to {}'.format(display_name, TEMP_DIR)
if len(opts) == 0 and len(sys.argv) == 2:
name = sys.argv[1].decode('utf8');
if not find_target_app(0, name):
print "can not find target app '%s'" % name
else:
dump_by_display_name(name)
sys.exit(0)
session = device.attach(pid)
script = load_js_file(session, DUMP_JS)
script.post('dump')
finished.wait()
if __name__ == "__main__":
try:
check_args()
pass
except KeyboardInterrupt:
if session:
session.detach()
sys.exit()
except:
pass
generate_ipa(PAYLOAD_PATH, ipa_name)
if session:
session.detach()
if __name__ == '__main__':
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('-o', '--output', dest='output_ipa', help='Specify name of the decrypted IPA')
parser.add_argument('target', nargs='?', help='Bundle identifier or display name of the target app')
args = parser.parse_args()
exit_code = 0
ssh = None
device = get_usb_iphone()
if args.list_applications:
list_applications(device)
else:
name_or_bundleid = args.target
output_ipa = args.output_ipa
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(Host, port=Port, username=User, password=Password)
create_dir(PAYLOAD_PATH)
(pid, display_name, bundle_identifier) = open_target_app(device, name_or_bundleid)
if output_ipa is None:
output_ipa = display_name
output_ipa = re.sub('\.ipa$', '', output_ipa)
if pid > 0:
start_dump(device, pid, output_ipa)
except paramiko.AuthenticationException as e:
print e
exit_code = 1
except Exception as e:
print('*** Caught exception: %s: %s' % (e.__class__, e))
traceback.print_exc()
exit_code = 1
if ssh:
ssh.close()
if os.path.exists(PAYLOAD_PATH):
shutil.rmtree(PAYLOAD_PATH)
sys.exit(exit_code)

58
process.sh Executable file
View File

@ -0,0 +1,58 @@
#!/bin/bash
ORIGINAL_IPA_FOLDER="${HOME}/Music/iTunes/iTunes Media/Mobile Applications"
DECRYPTED_IPA_FOLDER="${HOME}/Hacking/Decrypted Apps"
IOS_DEPLOY="/usr/local/bin/ios-deploy"
PLISTBUDDY="/usr/libexec/PlistBuddy"
FRIDA_DUMP_PY="./dump.py"
# CFBundleIdentifier in Info.plist to uninstall
ORIGINAL_IPAS=()
while IFS= read -r -d $'\0'; do
ORIGINAL_IPA=$( echo "${REPLY}" )
ORIGINAL_IPAS+=( "${ORIGINAL_IPA}" )
done < <(find "${ORIGINAL_IPA_FOLDER}" -type f -name '*.ipa' -print0)
echo "Decrypting ${#ORIGINAL_IPAS[@]} apps"
APP_COUNTER=0
for ORIGINAL_IPA in "${ORIGINAL_IPAS[@]}"; do
APP_COUNTER=$((APP_COUNTER + 1))
echo "#### Decryting app number: ${APP_COUNTER}"
APP_BUNDLE_ID=""
ESCAPED_IPA_PATH=$( echo "${ORIGINAL_IPA}" | sed 's/ /\\ /g' | sed 's/&/\\&/g' )
IPA_NAME=$( basename "${ESCAPED_IPA_PATH}" )
# Make a temp file for the iTunesMetadata.plist
ITUNESMETADATA_TMP_FILE=$( mktemp "/tmp/iTunesMetadata.plist.XXXXXX" ) || exit 1
# Unzip iTunesMetadata.plist to temp file
UNZIP_CMD="unzip -p ${ESCAPED_IPA_PATH} iTunesMetadata.plist > ${ITUNESMETADATA_TMP_FILE}"
echo "${UNZIP_CMD}"
eval "${UNZIP_CMD}" || exit 1
# Extract softwareVersionBundleId from plist file
APP_BUNDLE_ID=$( "${PLISTBUDDY}" -c 'Print softwareVersionBundleId' "${ITUNESMETADATA_TMP_FILE}" )
echo "App bundle identifier: ${APP_BUNDLE_ID}"
rm "${ITUNESMETADATA_TMP_FILE}"
# Install the app
IOS_DEPLOY_INSTALL="${IOS_DEPLOY} -W --bundle ${ESCAPED_IPA_PATH}"
echo "${IOS_DEPLOY_INSTALL}"
eval "${IOS_DEPLOY_INSTALL}"
if [ $? -eq 0 ]; then
sleep 1
echo "Dumping ${APP_BUNDLE_ID}"
FRIDA_DUMP_CMD="${FRIDA_DUMP_PY} --output ${IPA_NAME} ${APP_BUNDLE_ID}"
echo "${FRIDA_DUMP_CMD}"
eval "${FRIDA_DUMP_CMD}"
fi
# Uninstall the app
IOS_DEPLOY_UNINSTALL="${IOS_DEPLOY} -W --uninstall_only --bundle_id ${APP_BUNDLE_ID}"
echo "${IOS_DEPLOY_UNINSTALL}"
eval "${IOS_DEPLOY_UNINSTALL}"
done