1
1
#!/usr/bin/env python3
2
2
import subprocess , platform , os , sys , readline , time , argparse , requests , re
3
3
from urllib .parse import urlparse
4
- import cmd2 , click , frida , random , yaml
4
+ import cmd2 , click , frida , random , yaml , sys
5
5
from libraries .dumper import dump_pkg
6
6
from utils .google_trans_new import google_translator
7
7
from libraries .natives import *
20
20
BOLD = "\033 [;1m"
21
21
REVERSE = "\033 [;7m"
22
22
23
+ medusa_logo = """
24
+ ███╗ ███╗███████╗██████╗ ██╗ ██╗███████╗ █████╗
25
+ ████╗ ████║██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗
26
+ ██╔████╔██║█████╗ ██║ ██║██║ ██║███████╗███████║
27
+ ██║╚██╔╝██║██╔══╝ ██║ ██║██║ ██║╚════██║██╔══██║
28
+ ██║ ╚═╝ ██║███████╗██████╔╝╚██████╔╝███████║██║ ██║
29
+ ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ (Android) Version: 2.0
30
+
31
+ 🪼 Type help for options 🪼 \n \n
32
+ """
23
33
24
34
class Parser (cmd2 .Cmd ):
25
35
base_directory = os .path .dirname (__file__ )
@@ -43,6 +53,12 @@ class Parser(cmd2.Cmd):
43
53
modManager = ModuleManager ()
44
54
package_range = ''
45
55
56
+ interactive = True
57
+ time_to_run = None
58
+ package_name = None
59
+ save_to_file = None
60
+ device_id = None
61
+
46
62
def __init__ (self ):
47
63
super ().__init__ (
48
64
allow_cli_args = False
@@ -74,23 +90,38 @@ def preloop(self):
74
90
prog = 'Medusa' ,
75
91
description = 'An extensible and modularized framework that automates processes and techniques practiced during the dynamic analysis of Android Applications.' )
76
92
parser .add_argument ('-r' , '--recipe' , help = 'Use this option to load a session/recipe' )
93
+ parser .add_argument ('--not-interactive' , action = 'store_true' , help = 'Run Medusa without user interaction (additional parameters required)' )
94
+ parser .add_argument ('-t' , '--time' , type = int , help = 'Run Medusa for T seconds without user interaction' )
95
+ parser .add_argument ('-p' , '--package-name' , help = 'Package name to run' )
96
+ parser .add_argument ('-d' , '--device' , help = 'Device to connect to' )
97
+ parser .add_argument ('-s' , '--save' , help = 'Filename to save the output log' )
98
+
77
99
args = parser .parse_args ()
100
+ num_args_set = sum (1 for arg in vars (args ).values () if arg is not None )
78
101
79
102
if args .recipe :
80
- self .write_recipe (args .recipe )
103
+ if not args .not_interactive and num_args_set > 1 :
104
+ if args .time or args .device or args .save or args .package_name :
105
+ print ('Non-interactive mode arguments are ignored in interactive mode.' )
106
+ else :
107
+ self .write_recipe (args .recipe )
108
+ else :
109
+ if not (args .time and args .device and args .save and args .package_name ):
110
+ print ('Insufficient parameters for not interactive mode. Exiting...' )
111
+ exit (1 )
112
+ else :
113
+ self .interactive = False
114
+ self .time_to_run = args .time
115
+ self .package_name = args .package_name
116
+ self .device_id = args .device
117
+ self .save_to_file = args .save
118
+ self .write_recipe (args .recipe )
81
119
82
120
randomized_fg = lambda : tuple (random .randint (0 , 255 ) for _ in range (3 ))
83
-
84
- click .secho ("""
85
- ███╗ ███╗███████╗██████╗ ██╗ ██╗███████╗ █████╗
86
- ████╗ ████║██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗
87
- ██╔████╔██║█████╗ ██║ ██║██║ ██║███████╗███████║
88
- ██║╚██╔╝██║██╔══╝ ██║ ██║██║ ██║╚════██║██╔══██║
89
- ██║ ╚═╝ ██║███████╗██████╔╝╚██████╔╝███████║██║ ██║
90
- ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ (Android) Version: 2.0
91
-
92
- 🪼 Type help for options 🪼 \n \n """ , fg = randomized_fg (), bold = True )
121
+ click .secho (medusa_logo , fg = randomized_fg (), bold = True )
93
122
self .do_loaddevice ("dummy" )
123
+ if not self .interactive :
124
+ self .do_run ('-f ' + self .package_name )
94
125
95
126
###################################################### do_ defs start ############################################################
96
127
@@ -768,20 +799,24 @@ def do_loaddevice(self, line) -> None:
768
799
Load a device in order to interact
769
800
"""
770
801
try :
771
- print ('Available devices:\n ' )
772
- devices = frida .enumerate_devices ()
773
-
774
- for i in range (len (devices )):
775
- print (f'{ i } ) { devices [i ]} ' )
776
- self .device = devices [
777
- int (Numeric ('\n Enter the index of the device to use:' , lbound = 0 , ubound = len (devices ) - 1 ).ask ())]
778
- android_dev = android_device (self .device .id )
779
- android_dev .print_dev_properties ()
802
+ if self .interactive :
803
+ print ('Available devices:\n ' )
804
+ devices = frida .enumerate_devices ()
805
+
806
+ for i in range (len (devices )):
807
+ print (f'{ i } ) { devices [i ]} ' )
808
+ self .device = devices [
809
+ int (Numeric ('\n Enter the index of the device to use:' , lbound = 0 , ubound = len (devices ) - 1 ).ask ())]
810
+ android_dev = android_device (self .device .id )
811
+ android_dev .print_dev_properties ()
812
+ else :
813
+ self .device = frida .get_device (self .device_id )
780
814
except :
781
815
self .device = frida .get_remote_device ()
782
816
finally :
783
817
# lets start by loading all packages and let the user to filter them out
784
- self .init_packages ('-3' )
818
+ if self .interactive :
819
+ self .init_packages ('-3' )
785
820
786
821
def do_memops (self , line ) -> None :
787
822
"""
@@ -1014,6 +1049,11 @@ def do_run(self, line) -> None:
1014
1049
add --host ip:port to specify the IP address and port of the remote Frida server to connect to.
1015
1050
"""
1016
1051
try :
1052
+ if not self .interactive :
1053
+ self .do_compile (line )
1054
+ self .run_frida_n_interactive (True , False , line .split (' ' )[1 ], self .device , - 1 , '' , '' )
1055
+ return
1056
+
1017
1057
if self .modified :
1018
1058
if Polar ('Module list has been modified, do you want to recompile?' ).ask ():
1019
1059
self .do_compile (line )
@@ -1588,6 +1628,50 @@ def print_app_info(self) -> None:
1588
1628
else :
1589
1629
print ("[!] No available info." )
1590
1630
1631
+ def run_frida_n_interactive (self , force , detached , package_name , device , pid = - 1 , host = '' , port = '' ) -> None :
1632
+ if host != '' and port != '' :
1633
+ device = frida .get_device_manager () \
1634
+ .add_remote_device (f'{ host } :{ port } ' )
1635
+ print (f'Using device:{ device } ' )
1636
+
1637
+ self .detached = False
1638
+ session = self .frida_session_handler (device , force , package_name , pid )
1639
+ try :
1640
+ with open (os .path .join (self .base_directory , "agent.js" )) as f :
1641
+ self .script = session .create_script (f .read ())
1642
+
1643
+ session .on ('detached' , self .on_detached )
1644
+ self .script .on ("message" , self .my_message_handler ) # register the message handler
1645
+ self .script .load ()
1646
+ if force :
1647
+ device .resume (self .pid )
1648
+
1649
+ startTime = time .time ()
1650
+ end_time = self .time_to_run + startTime
1651
+ original_stdout = sys .stdout
1652
+ from io import StringIO
1653
+ temp_stdout = StringIO ()
1654
+ sys .stdout = temp_stdout
1655
+
1656
+ while (time .time () < end_time ) and (not self .detached ):
1657
+ pass
1658
+
1659
+ with open (self .save_to_file ,'w' ) as f :
1660
+ sys .stdout = original_stdout
1661
+ f .write (medusa_logo )
1662
+ f .write (temp_stdout .getvalue ())
1663
+
1664
+ if self .script :
1665
+ self .script .unload ()
1666
+ open (os .path .join (self .base_directory , 'agent.js' ), 'w' ).close ()
1667
+ self .edit_scratchpad ('' )
1668
+ sys .exit (0 )
1669
+
1670
+ except Exception as e :
1671
+ print (e )
1672
+ print (RESET )
1673
+
1674
+
1591
1675
def run_frida (self , force , detached , package_name , device , pid = - 1 , host = '' , port = '' ) -> None :
1592
1676
if host != '' and port != '' :
1593
1677
device = frida .get_device_manager () \
0 commit comments