Skip to content

Commit f738c1c

Browse files
author
Ch0pin
committed
Run medusa in non interactive mode
1 parent 347bec2 commit f738c1c

File tree

2 files changed

+133
-29
lines changed

2 files changed

+133
-29
lines changed

medusa

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,38 @@
22

33
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]:-$0}";)" &> /dev/null && pwd 2> /dev/null)"
44

5+
# Check for --not-interactive flag
6+
not_interactive=0
7+
for arg in "$@"; do
8+
if [ "$arg" = "--not-interactive" ]; then
9+
not_interactive=1
10+
break
11+
fi
12+
done
13+
14+
# Main logic
515
if [ "$#" -eq 0 ]; then
616
python3 "$SCRIPT_DIR/medusa.py"
7-
elif [ "$#" -eq 1 ]; then
8-
if [ "$1" == "--ios" ]; then
17+
elif [ "$1" = "--ios" ]; then
18+
if [ "$#" -eq 1 ]; then
919
python3 "$SCRIPT_DIR/medusa_ios.py"
20+
elif [ "$#" -eq 3 ] && [ "$2" = "-r" ]; then
21+
python3 "$SCRIPT_DIR/medusa_ios.py" -r "$3"
1022
else
11-
echo "Invalid argument. Use '--ios' to run medusa for iOS."
23+
echo "Invalid argument combination for iOS. Usage: $0 [--ios] [-r filename]"
1224
fi
13-
elif [ "$#" -eq 3 ] && [ "$1" == "-r" ] && [ "$3" == "--ios" ]; then
14-
python3 "$SCRIPT_DIR/medusa_ios.py" -r "$2"
15-
elif [ "$#" -eq 2 ] && [ "$1" == "-r" ]; then
25+
elif [ "$1" = "-r" ] && [ "$#" -eq 2 ] && [ "$not_interactive" -eq 0 ]; then
1626
python3 "$SCRIPT_DIR/medusa.py" -r "$2"
27+
elif [ "$not_interactive" -eq 1 ]; then
28+
# Ensure --ios is not used with --not-interactive
29+
for arg in "$@"; do
30+
if [ "$arg" = "--ios" ]; then
31+
echo "The --not-interactive flag is not applicable for iOS. Please remove --ios or --not-interactive."
32+
exit 1
33+
fi
34+
done
35+
# Proceed with medusa.py in not interactive mode
36+
python3 "$SCRIPT_DIR/medusa.py" "$@"
1737
else
18-
echo "Usage: $0 [-r filename] [--ios]"
38+
echo "Usage: $0 [--ios] [-r filename] [--not-interactive [other required arguments]]"
1939
fi

medusa.py

Lines changed: 106 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3
22
import subprocess, platform, os, sys, readline, time, argparse, requests, re
33
from urllib.parse import urlparse
4-
import cmd2, click, frida, random, yaml
4+
import cmd2, click, frida, random, yaml, sys
55
from libraries.dumper import dump_pkg
66
from utils.google_trans_new import google_translator
77
from libraries.natives import *
@@ -20,6 +20,16 @@
2020
BOLD = "\033[;1m"
2121
REVERSE = "\033[;7m"
2222

23+
medusa_logo="""
24+
███╗ ███╗███████╗██████╗ ██╗ ██╗███████╗ █████╗
25+
████╗ ████║██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗
26+
██╔████╔██║█████╗ ██║ ██║██║ ██║███████╗███████║
27+
██║╚██╔╝██║██╔══╝ ██║ ██║██║ ██║╚════██║██╔══██║
28+
██║ ╚═╝ ██║███████╗██████╔╝╚██████╔╝███████║██║ ██║
29+
╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ (Android) Version: 2.0
30+
31+
🪼 Type help for options 🪼 \n\n
32+
"""
2333

2434
class Parser(cmd2.Cmd):
2535
base_directory = os.path.dirname(__file__)
@@ -43,6 +53,12 @@ class Parser(cmd2.Cmd):
4353
modManager = ModuleManager()
4454
package_range = ''
4555

56+
interactive=True
57+
time_to_run = None
58+
package_name = None
59+
save_to_file = None
60+
device_id = None
61+
4662
def __init__(self):
4763
super().__init__(
4864
allow_cli_args=False
@@ -74,23 +90,38 @@ def preloop(self):
7490
prog='Medusa',
7591
description='An extensible and modularized framework that automates processes and techniques practiced during the dynamic analysis of Android Applications.')
7692
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+
7799
args = parser.parse_args()
100+
num_args_set = sum(1 for arg in vars(args).values() if arg is not None)
78101

79102
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)
81119

82120
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)
93122
self.do_loaddevice("dummy")
123+
if not self.interactive:
124+
self.do_run('-f '+self.package_name)
94125

95126
###################################################### do_ defs start ############################################################
96127

@@ -768,20 +799,24 @@ def do_loaddevice(self, line) -> None:
768799
Load a device in order to interact
769800
"""
770801
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('\nEnter 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('\nEnter 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)
780814
except:
781815
self.device = frida.get_remote_device()
782816
finally:
783817
# 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')
785820

786821
def do_memops(self, line) -> None:
787822
"""
@@ -1014,6 +1049,11 @@ def do_run(self, line) -> None:
10141049
add --host ip:port to specify the IP address and port of the remote Frida server to connect to.
10151050
"""
10161051
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+
10171057
if self.modified:
10181058
if Polar('Module list has been modified, do you want to recompile?').ask():
10191059
self.do_compile(line)
@@ -1588,6 +1628,50 @@ def print_app_info(self) -> None:
15881628
else:
15891629
print("[!] No available info.")
15901630

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+
15911675
def run_frida(self, force, detached, package_name, device, pid=-1, host='', port='') -> None:
15921676
if host != '' and port != '':
15931677
device = frida.get_device_manager() \

0 commit comments

Comments
 (0)