Aarivex

[Frida] dump_winrar.py

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