Aarivex

[Frida] dump_win.py

Oct 6th, 2019
1,152
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.90 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. # Author : AloneMonkey
  5. # blog: www.alonemonkey.com
  6.  
  7. # Rewritten for Windows by Aarivex, aarivex.dev
  8. # Uses 7zip - https://www.7-zip.org/
  9.  
  10. import sys
  11. import codecs
  12. import frida
  13. import threading
  14. import os
  15. import shutil
  16. import time
  17. import argparse
  18. import tempfile
  19. import subprocess
  20. import re
  21.  
  22. import paramiko
  23. from paramiko import SSHClient
  24. from scp import SCPClient
  25. from tqdm import tqdm
  26. import traceback
  27.  
  28. reload(sys)
  29. sys.setdefaultencoding('utf8')
  30.  
  31. script_dir = os.path.dirname(os.path.realpath(__file__))
  32.  
  33. DUMP_JS = os.path.join(script_dir, 'dump.js')
  34.  
  35. User = 'root'
  36. Password = 'alpine'
  37. Host = 'localhost'
  38. Port = 22
  39.  
  40. TEMP_DIR = tempfile.gettempdir()
  41. PAYLOAD_DIR = 'Payload'
  42. PAYLOAD_PATH = os.path.join(TEMP_DIR, PAYLOAD_DIR)
  43. file_dict = {}
  44.  
  45. finished = threading.Event()
  46.  
  47.  
  48. def get_usb_iphone():
  49.     Type = 'usb'
  50.     if int(frida.__version__.split('.')[0]) < 12:
  51.         Type = 'tether'
  52.        
  53.     device_manager = frida.get_device_manager()
  54.     changed = threading.Event()
  55.  
  56.     def on_changed():
  57.         changed.set()
  58.  
  59.     device_manager.on('changed', on_changed)
  60.  
  61.     device = None
  62.     while device is None:
  63.         devices = [dev for dev in device_manager.enumerate_devices() if dev.type == Type]
  64.         if len(devices) == 0:
  65.             print 'Waiting for USB device...'
  66.             changed.wait()
  67.         else:
  68.             device = devices[0]
  69.  
  70.     device_manager.off('changed', on_changed)
  71.  
  72.     return device
  73.  
  74.  
  75. def generate_ipa(path, display_name):
  76.     ipa_filename = display_name + '.ipa'
  77.  
  78.     print 'Generating "{}"'.format(ipa_filename)
  79.     try:
  80.         app_name = file_dict['app']
  81.  
  82.         for key, value in file_dict.items():
  83.             from_dir = os.path.join(path, key)
  84.             to_dir = os.path.join(path, app_name, value)
  85.             if key != 'app':
  86.                 shutil.move(from_dir, to_dir)
  87.  
  88.         target_dir = './' + PAYLOAD_DIR
  89.         zip_args = (os.getcwd() + '\\7za', 'a', '-r', os.path.join(os.getcwd(), ipa_filename), target_dir)
  90.         subprocess.check_call(zip_args, cwd=TEMP_DIR, shell=True)
  91.         shutil.rmtree(PAYLOAD_PATH)
  92.         print
  93.     except Exception as e:
  94.         print e
  95.         finished.set()
  96.  
  97. def on_message(message, data):
  98.     t = tqdm(unit='B',unit_scale=True,unit_divisor=1024,miniters=1)
  99.     last_sent = [0]
  100.  
  101.     def progress(filename, size, sent):
  102.         t.desc = os.path.basename(filename)
  103.         t.total = size
  104.         t.update(sent - last_sent[0])
  105.         last_sent[0] = 0 if size == sent else sent
  106.  
  107.     if 'payload' in message:
  108.         payload = message['payload']
  109.         if 'dump' in payload:
  110.             origin_path = payload['path']
  111.             dump_path = payload['dump']
  112.  
  113.             scp_from = dump_path
  114.             scp_to = PAYLOAD_PATH + u'/'
  115.  
  116.             with SCPClient(ssh.get_transport(), progress = progress, socket_timeout = 60) as scp:
  117.                 scp.get(scp_from, scp_to)
  118.  
  119.             chmod_dir = os.path.join(PAYLOAD_PATH, os.path.basename(dump_path))
  120.             chmod_args = ('chmod', '655', chmod_dir)
  121.             try:
  122.                 subprocess.check_call(chmod_args)
  123.             except subprocess.CalledProcessError as err:
  124.                 print err
  125.  
  126.             index = origin_path.find('.app/')
  127.             file_dict[os.path.basename(dump_path)] = origin_path[index + 5:]
  128.  
  129.         if 'app' in payload:
  130.             app_path = payload['app']
  131.  
  132.             scp_from = app_path
  133.             scp_to = PAYLOAD_PATH + u'/'
  134.             with SCPClient(ssh.get_transport(), progress = progress, socket_timeout = 60) as scp:
  135.                 scp.get(scp_from, scp_to, recursive=True)
  136.  
  137.             chmod_dir = os.path.join(PAYLOAD_PATH, os.path.basename(app_path))
  138.             chmod_args = ('chmod', '755', chmod_dir)
  139.             try:
  140.                 subprocess.check_call(chmod_args)
  141.             except subprocess.CalledProcessError as err:
  142.                 print err
  143.  
  144.             file_dict['app'] = os.path.basename(app_path)
  145.  
  146.         if 'done' in payload:
  147.             finished.set()
  148.     t.close()
  149.  
  150. def compare_applications(a, b):
  151.     a_is_running = a.pid != 0
  152.     b_is_running = b.pid != 0
  153.     if a_is_running == b_is_running:
  154.         if a.name > b.name:
  155.             return 1
  156.         elif a.name < b.name:
  157.             return -1
  158.         else:
  159.             return 0
  160.     elif a_is_running:
  161.         return -1
  162.     else:
  163.         return 1
  164.  
  165.  
  166. def cmp_to_key(mycmp):
  167.     """Convert a cmp= function into a key= function"""
  168.  
  169.     class K:
  170.         def __init__(self, obj):
  171.             self.obj = obj
  172.  
  173.         def __lt__(self, other):
  174.             return mycmp(self.obj, other.obj) < 0
  175.  
  176.         def __gt__(self, other):
  177.             return mycmp(self.obj, other.obj) > 0
  178.  
  179.         def __eq__(self, other):
  180.             return mycmp(self.obj, other.obj) == 0
  181.  
  182.         def __le__(self, other):
  183.             return mycmp(self.obj, other.obj) <= 0
  184.  
  185.         def __ge__(self, other):
  186.             return mycmp(self.obj, other.obj) >= 0
  187.  
  188.         def __ne__(self, other):
  189.             return mycmp(self.obj, other.obj) != 0
  190.  
  191.     return K
  192.  
  193.  
  194. def get_applications(device):
  195.     try:
  196.         applications = device.enumerate_applications()
  197.     except Exception as e:
  198.         print 'Failed to enumerate applications: %s' % e
  199.         return
  200.  
  201.     return applications
  202.  
  203.  
  204. def list_applications(device):
  205.     applications = get_applications(device)
  206.  
  207.     if len(applications) > 0:
  208.         pid_column_width = max(map(lambda app: len('{}'.format(app.pid)), applications))
  209.         name_column_width = max(map(lambda app: len(app.name), applications))
  210.         identifier_column_width = max(map(lambda app: len(app.identifier), applications))
  211.     else:
  212.         pid_column_width = 0
  213.         name_column_width = 0
  214.         identifier_column_width = 0
  215.  
  216.     header_format = '%' + str(pid_column_width) + 's  ' + '%-' + str(name_column_width) + 's  ' + '%-' + str(
  217.         identifier_column_width) + 's'
  218.     print header_format % ('PID', 'Name', 'Identifier')
  219.     print '%s  %s  %s' % (pid_column_width * '-', name_column_width * '-', identifier_column_width * '-')
  220.     line_format = '%' + str(pid_column_width) + 's  ' + '%-' + str(name_column_width) + 's  ' + '%-' + str(
  221.         identifier_column_width) + 's'
  222.     for application in sorted(applications, key=cmp_to_key(compare_applications)):
  223.         if application.pid == 0:
  224.             print line_format % ('-', application.name, application.identifier)
  225.         else:
  226.             print line_format % (application.pid, application.name, application.identifier)
  227.  
  228.  
  229. def load_js_file(session, filename):
  230.     source = ''
  231.     with codecs.open(filename, 'r', 'utf-8') as f:
  232.         source = source + f.read()
  233.     script = session.create_script(source)
  234.     script.on('message', on_message)
  235.     script.load()
  236.  
  237.     return script
  238.  
  239.  
  240. def create_dir(path):
  241.     path = path.strip()
  242.     path = path.rstrip('\\')
  243.     if os.path.exists(path):
  244.         shutil.rmtree(path)
  245.     try:
  246.         os.makedirs(path)
  247.     except os.error as err:
  248.         print err
  249.  
  250.  
  251. def open_target_app(device, name_or_bundleid):
  252.     print 'Start the target app {}'.format(name_or_bundleid)
  253.  
  254.     pid = ''
  255.     session = None
  256.     display_name = ''
  257.     bundle_identifier = ''
  258.     for application in get_applications(device):
  259.         if name_or_bundleid == application.identifier or name_or_bundleid == application.name:
  260.             pid = application.pid
  261.             display_name = application.name
  262.             bundle_identifier = application.identifier
  263.  
  264.     try:
  265.         if not pid:
  266.             pid = device.spawn([bundle_identifier])
  267.             session = device.attach(pid)
  268.             device.resume(pid)
  269.         else:
  270.             session = device.attach(pid)
  271.     except Exception as e:
  272.         print e
  273.  
  274.     return session, display_name, bundle_identifier
  275.  
  276.  
  277. def start_dump(session, ipa_name):
  278.     print 'Dumping {} to {}'.format(display_name, TEMP_DIR)
  279.  
  280.     script = load_js_file(session, DUMP_JS)
  281.     script.post('dump')
  282.     finished.wait()
  283.  
  284.     generate_ipa(PAYLOAD_PATH, ipa_name)
  285.  
  286.     if session:
  287.         session.detach()
  288.  
  289.  
  290. if __name__ == '__main__':
  291.     parser = argparse.ArgumentParser(description='frida-ios-dump (by AloneMonkey v2.0)')
  292.     parser.add_argument('-l', '--list', dest='list_applications', action='store_true', help='List the installed apps')
  293.     parser.add_argument('-o', '--output', dest='output_ipa', help='Specify name of the decrypted IPA')
  294.     parser.add_argument('target', nargs='?', help='Bundle identifier or display name of the target app')
  295.     args = parser.parse_args()
  296.  
  297.     exit_code = 0
  298.     ssh = None
  299.     device = get_usb_iphone()
  300.  
  301.     if len(sys.argv[1:]) == 0:
  302.         parser.print_help()
  303.     elif args.list_applications:
  304.         list_applications(device)
  305.     else:
  306.         name_or_bundleid = args.target
  307.         output_ipa = args.output_ipa
  308.  
  309.         try:
  310.             ssh = paramiko.SSHClient()
  311.             ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  312.             ssh.connect(Host, port=Port, username=User, password=Password)
  313.  
  314.             create_dir(PAYLOAD_PATH)
  315.             (session, display_name, bundle_identifier) = open_target_app(device, name_or_bundleid)
  316.             if output_ipa is None:
  317.                 output_ipa = display_name
  318.             output_ipa = re.sub('\.ipa$', '', output_ipa)
  319.             if session:
  320.                 start_dump(session, output_ipa)
  321.         except paramiko.ssh_exception.NoValidConnectionsError as e:
  322.             print e
  323.             exit_code = 1
  324.         except paramiko.AuthenticationException as e:
  325.             print e
  326.             exit_code = 1
  327.         except Exception as e:
  328.             print('*** Caught exception: %s: %s' % (e.__class__, e))
  329.             traceback.print_exc()
  330.             exit_code = 1
  331.  
  332.     if ssh:
  333.         ssh.close()
  334.  
  335.     if os.path.exists(PAYLOAD_PATH):
  336.         shutil.rmtree(PAYLOAD_PATH)
  337.  
  338.     sys.exit(exit_code)
Add Comment
Please, Sign In to add comment