Autarch/web/routes/android_exploit.py

985 lines
30 KiB
Python
Raw Normal View History

"""Android Exploitation routes - App extraction, recon, payloads, boot, root."""
import os
from flask import Blueprint, render_template, request, jsonify
from web.auth import login_required
android_exploit_bp = Blueprint('android_exploit', __name__, url_prefix='/android-exploit')
def _get_mgr():
from core.android_exploit import get_exploit_manager
return get_exploit_manager()
def _get_serial():
data = request.get_json(silent=True) or {}
serial = data.get('serial', '').strip()
if not serial:
# Auto-detect if only one device connected
devices = _get_mgr().hw.adb_devices()
online = [d for d in devices if d.get('state') == 'device']
if len(online) == 1:
return online[0]['serial'], None
return None, jsonify({'error': 'No device serial provided (and multiple/no devices found)'})
return serial, None
@android_exploit_bp.route('/')
@login_required
def index():
from core.hardware import get_hardware_manager
hw = get_hardware_manager()
status = hw.get_status()
return render_template('android_exploit.html', status=status)
# ── App Extraction ────────────────────────────────────────────────
@android_exploit_bp.route('/apps/list', methods=['POST'])
@login_required
def apps_list():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
include_system = data.get('include_system', False)
return jsonify(_get_mgr().list_packages(serial, include_system=include_system))
@android_exploit_bp.route('/apps/pull-apk', methods=['POST'])
@login_required
def apps_pull_apk():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
package = data.get('package', '').strip()
if not package:
return jsonify({'error': 'No package provided'})
return jsonify(_get_mgr().pull_apk(serial, package))
@android_exploit_bp.route('/apps/pull-data', methods=['POST'])
@login_required
def apps_pull_data():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
package = data.get('package', '').strip()
if not package:
return jsonify({'error': 'No package provided'})
return jsonify(_get_mgr().pull_app_data(serial, package))
@android_exploit_bp.route('/apps/shared-prefs', methods=['POST'])
@login_required
def apps_shared_prefs():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
package = data.get('package', '').strip()
if not package:
return jsonify({'error': 'No package provided'})
return jsonify(_get_mgr().extract_shared_prefs(serial, package))
# ── Device Recon ──────────────────────────────────────────────────
@android_exploit_bp.route('/recon/device-dump', methods=['POST'])
@login_required
def recon_device_dump():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().full_device_dump(serial))
@android_exploit_bp.route('/recon/accounts', methods=['POST'])
@login_required
def recon_accounts():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().get_accounts(serial))
@android_exploit_bp.route('/recon/wifi', methods=['POST'])
@login_required
def recon_wifi():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().get_wifi_passwords(serial))
@android_exploit_bp.route('/recon/calls', methods=['POST'])
@login_required
def recon_calls():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
limit = int(data.get('limit', 200))
return jsonify(_get_mgr().extract_call_logs(serial, limit=limit))
@android_exploit_bp.route('/recon/sms', methods=['POST'])
@login_required
def recon_sms():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
limit = int(data.get('limit', 200))
return jsonify(_get_mgr().extract_sms(serial, limit=limit))
@android_exploit_bp.route('/recon/contacts', methods=['POST'])
@login_required
def recon_contacts():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().extract_contacts(serial))
@android_exploit_bp.route('/recon/browser', methods=['POST'])
@login_required
def recon_browser():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().extract_browser_history(serial))
@android_exploit_bp.route('/recon/credentials', methods=['POST'])
@login_required
def recon_credentials():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().extract_saved_credentials(serial))
@android_exploit_bp.route('/recon/export', methods=['POST'])
@login_required
def recon_export():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().export_recon_report(serial))
# ── Payload Deployment ────────────────────────────────────────────
@android_exploit_bp.route('/payload/deploy', methods=['POST'])
@login_required
def payload_deploy():
serial = request.form.get('serial', '').strip()
if not serial:
return jsonify({'error': 'No serial provided'})
remote_path = request.form.get('remote_path', '/data/local/tmp/').strip()
f = request.files.get('file')
if not f:
return jsonify({'error': 'No file uploaded'})
from core.paths import get_uploads_dir
upload_path = str(get_uploads_dir() / f.filename)
f.save(upload_path)
return jsonify(_get_mgr().deploy_binary(serial, upload_path, remote_path))
@android_exploit_bp.route('/payload/execute', methods=['POST'])
@login_required
def payload_execute():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
remote_path = data.get('remote_path', '').strip()
args = data.get('args', '')
background = data.get('background', True)
if not remote_path:
return jsonify({'error': 'No remote_path provided'})
return jsonify(_get_mgr().execute_payload(serial, remote_path, args=args, background=background))
@android_exploit_bp.route('/payload/reverse-shell', methods=['POST'])
@login_required
def payload_reverse_shell():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
lhost = data.get('lhost', '').strip()
lport = data.get('lport', '')
method = data.get('method', 'nc').strip()
if not lhost or not lport:
return jsonify({'error': 'Missing lhost or lport'})
return jsonify(_get_mgr().setup_reverse_shell(serial, lhost, int(lport), method))
@android_exploit_bp.route('/payload/persistence', methods=['POST'])
@login_required
def payload_persistence():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
method = data.get('method', 'init.d').strip()
return jsonify(_get_mgr().install_persistence(serial, method))
@android_exploit_bp.route('/payload/list', methods=['POST'])
@login_required
def payload_list():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().list_running_payloads(serial))
@android_exploit_bp.route('/payload/kill', methods=['POST'])
@login_required
def payload_kill():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
pid = data.get('pid', '').strip()
if not pid:
return jsonify({'error': 'No PID provided'})
return jsonify(_get_mgr().kill_payload(serial, pid))
# ── Boot / Recovery ───────────────────────────────────────────────
@android_exploit_bp.route('/boot/info', methods=['POST'])
@login_required
def boot_info():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().get_bootloader_info(serial))
@android_exploit_bp.route('/boot/backup', methods=['POST'])
@login_required
def boot_backup():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().backup_boot_image(serial))
@android_exploit_bp.route('/boot/unlock', methods=['POST'])
@login_required
def boot_unlock():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().unlock_bootloader(serial))
@android_exploit_bp.route('/boot/flash-recovery', methods=['POST'])
@login_required
def boot_flash_recovery():
serial = request.form.get('serial', '').strip()
if not serial:
return jsonify({'error': 'No serial provided'})
f = request.files.get('file')
if not f:
return jsonify({'error': 'No file uploaded'})
from core.paths import get_uploads_dir
upload_path = str(get_uploads_dir() / f.filename)
f.save(upload_path)
return jsonify(_get_mgr().flash_recovery(serial, upload_path))
@android_exploit_bp.route('/boot/flash-boot', methods=['POST'])
@login_required
def boot_flash_boot():
serial = request.form.get('serial', '').strip()
if not serial:
return jsonify({'error': 'No serial provided'})
f = request.files.get('file')
if not f:
return jsonify({'error': 'No file uploaded'})
from core.paths import get_uploads_dir
upload_path = str(get_uploads_dir() / f.filename)
f.save(upload_path)
return jsonify(_get_mgr().flash_boot(serial, upload_path))
@android_exploit_bp.route('/boot/disable-verity', methods=['POST'])
@login_required
def boot_disable_verity():
# Supports both JSON and form upload
if request.content_type and 'multipart' in request.content_type:
serial = request.form.get('serial', '').strip()
f = request.files.get('file')
vbmeta = None
if f:
from core.paths import get_uploads_dir
vbmeta = str(get_uploads_dir() / f.filename)
f.save(vbmeta)
else:
data = request.get_json(silent=True) or {}
serial = data.get('serial', '').strip()
vbmeta = None
if not serial:
return jsonify({'error': 'No serial provided'})
return jsonify(_get_mgr().disable_verity(serial, vbmeta))
@android_exploit_bp.route('/boot/temp-boot', methods=['POST'])
@login_required
def boot_temp():
serial = request.form.get('serial', '').strip()
if not serial:
return jsonify({'error': 'No serial provided'})
f = request.files.get('file')
if not f:
return jsonify({'error': 'No file uploaded'})
from core.paths import get_uploads_dir
upload_path = str(get_uploads_dir() / f.filename)
f.save(upload_path)
return jsonify(_get_mgr().boot_temp(serial, upload_path))
# ── Root Methods ──────────────────────────────────────────────────
@android_exploit_bp.route('/root/check', methods=['POST'])
@login_required
def root_check():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().check_root(serial))
@android_exploit_bp.route('/root/install-magisk', methods=['POST'])
@login_required
def root_install_magisk():
serial = request.form.get('serial', '').strip()
if not serial:
return jsonify({'error': 'No serial provided'})
f = request.files.get('file')
if not f:
return jsonify({'error': 'No file uploaded'})
from core.paths import get_uploads_dir
upload_path = str(get_uploads_dir() / f.filename)
f.save(upload_path)
return jsonify(_get_mgr().install_magisk(serial, upload_path))
@android_exploit_bp.route('/root/pull-patched', methods=['POST'])
@login_required
def root_pull_patched():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().pull_patched_boot(serial))
@android_exploit_bp.route('/root/exploit', methods=['POST'])
@login_required
def root_exploit():
serial = request.form.get('serial', '').strip()
if not serial:
return jsonify({'error': 'No serial provided'})
f = request.files.get('file')
if not f:
return jsonify({'error': 'No file uploaded'})
from core.paths import get_uploads_dir
upload_path = str(get_uploads_dir() / f.filename)
f.save(upload_path)
return jsonify(_get_mgr().root_via_exploit(serial, upload_path))
# ── SMS Manipulation ─────────────────────────────────────────────
@android_exploit_bp.route('/sms/list', methods=['POST'])
@login_required
def sms_list():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
limit = int(data.get('limit', 50))
address = data.get('address', '').strip() or None
return jsonify(_get_mgr().sms_list(serial, limit=limit, address=address))
@android_exploit_bp.route('/sms/insert', methods=['POST'])
@login_required
def sms_insert():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
address = data.get('address', '').strip()
body = data.get('body', '').strip()
if not address or not body:
return jsonify({'error': 'Missing address or body'})
return jsonify(_get_mgr().sms_insert(
serial, address, body,
date_str=data.get('date') or None,
time_str=data.get('time') or None,
msg_type=data.get('type', 'inbox'),
read=data.get('read', True),
))
@android_exploit_bp.route('/sms/bulk-insert', methods=['POST'])
@login_required
def sms_bulk_insert():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
messages = data.get('messages', [])
if not messages:
return jsonify({'error': 'No messages provided'})
return jsonify(_get_mgr().sms_bulk_insert(serial, messages))
@android_exploit_bp.route('/sms/update', methods=['POST'])
@login_required
def sms_update():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
sms_id = data.get('id', '').strip()
if not sms_id:
return jsonify({'error': 'No SMS id provided'})
return jsonify(_get_mgr().sms_update(
serial, sms_id,
body=data.get('body'),
date_str=data.get('date'),
time_str=data.get('time'),
address=data.get('address'),
msg_type=data.get('type'),
read=data.get('read'),
))
@android_exploit_bp.route('/sms/delete', methods=['POST'])
@login_required
def sms_delete():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
sms_id = data.get('id', '').strip() if data.get('id') else None
address = data.get('address', '').strip() if data.get('address') else None
delete_all_from = data.get('delete_all_from', False)
return jsonify(_get_mgr().sms_delete(serial, sms_id=sms_id, address=address,
delete_all_from=delete_all_from))
@android_exploit_bp.route('/sms/delete-all', methods=['POST'])
@login_required
def sms_delete_all():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().sms_delete_all(serial))
# ── RCS Spoofing ─────────────────────────────────────────────────
@android_exploit_bp.route('/rcs/check', methods=['POST'])
@login_required
def rcs_check():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().rcs_check_support(serial))
@android_exploit_bp.route('/rcs/list', methods=['POST'])
@login_required
def rcs_list():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
limit = int(data.get('limit', 50))
return jsonify(_get_mgr().rcs_list(serial, limit=limit))
@android_exploit_bp.route('/rcs/insert', methods=['POST'])
@login_required
def rcs_insert():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
address = data.get('address', '').strip()
body = data.get('body', '').strip()
if not address or not body:
return jsonify({'error': 'Missing address or body'})
return jsonify(_get_mgr().rcs_insert(
serial, address, body,
date_str=data.get('date') or None,
time_str=data.get('time') or None,
sender_name=data.get('sender_name') or None,
is_outgoing=data.get('is_outgoing', False),
))
@android_exploit_bp.route('/rcs/delete', methods=['POST'])
@login_required
def rcs_delete():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
msg_id = data.get('id', '')
if not msg_id:
return jsonify({'error': 'No message id provided'})
return jsonify(_get_mgr().rcs_delete(serial, int(msg_id)))
# ── Screen & Input ───────────────────────────────────────────────
@android_exploit_bp.route('/screen/capture', methods=['POST'])
@login_required
def screen_capture():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().screen_capture(serial))
@android_exploit_bp.route('/screen/record', methods=['POST'])
@login_required
def screen_record():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
duration = int(data.get('duration', 10))
return jsonify(_get_mgr().screen_record(serial, duration=duration))
@android_exploit_bp.route('/screen/tap', methods=['POST'])
@login_required
def screen_tap():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
return jsonify(_get_mgr().input_tap(serial, data.get('x', 0), data.get('y', 0)))
@android_exploit_bp.route('/screen/swipe', methods=['POST'])
@login_required
def screen_swipe():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
return jsonify(_get_mgr().input_swipe(serial, data.get('x1',0), data.get('y1',0),
data.get('x2',0), data.get('y2',0),
int(data.get('duration', 300))))
@android_exploit_bp.route('/screen/text', methods=['POST'])
@login_required
def screen_text():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
text = data.get('text', '')
if not text:
return jsonify({'error': 'No text provided'})
return jsonify(_get_mgr().input_text(serial, text))
@android_exploit_bp.route('/screen/key', methods=['POST'])
@login_required
def screen_key():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
return jsonify(_get_mgr().input_keyevent(serial, data.get('keycode', 3)))
@android_exploit_bp.route('/screen/keylogger-start', methods=['POST'])
@login_required
def keylogger_start():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().start_keylogger(serial))
@android_exploit_bp.route('/screen/keylogger-stop', methods=['POST'])
@login_required
def keylogger_stop():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().stop_keylogger(serial))
@android_exploit_bp.route('/screen/dismiss-lock', methods=['POST'])
@login_required
def dismiss_lock():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().dismiss_lockscreen(serial))
@android_exploit_bp.route('/screen/disable-lock', methods=['POST'])
@login_required
def disable_lock():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().disable_lockscreen(serial))
# ── Advanced: Data ───────────────────────────────────────────────
@android_exploit_bp.route('/adv/clipboard', methods=['POST'])
@login_required
def adv_clipboard():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().extract_clipboard(serial))
@android_exploit_bp.route('/adv/notifications', methods=['POST'])
@login_required
def adv_notifications():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().dump_notifications(serial))
@android_exploit_bp.route('/adv/location', methods=['POST'])
@login_required
def adv_location():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().extract_location(serial))
@android_exploit_bp.route('/adv/media-list', methods=['POST'])
@login_required
def adv_media_list():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
return jsonify(_get_mgr().extract_media_list(serial, media_type=data.get('type', 'photos')))
@android_exploit_bp.route('/adv/media-pull', methods=['POST'])
@login_required
def adv_media_pull():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
return jsonify(_get_mgr().pull_media_folder(serial, media_type=data.get('type', 'photos'),
limit=int(data.get('limit', 50))))
@android_exploit_bp.route('/adv/whatsapp', methods=['POST'])
@login_required
def adv_whatsapp():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().extract_whatsapp_db(serial))
@android_exploit_bp.route('/adv/telegram', methods=['POST'])
@login_required
def adv_telegram():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().extract_telegram_db(serial))
@android_exploit_bp.route('/adv/signal', methods=['POST'])
@login_required
def adv_signal():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().extract_signal_db(serial))
@android_exploit_bp.route('/adv/fingerprint', methods=['POST'])
@login_required
def adv_fingerprint():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().get_device_fingerprint(serial))
@android_exploit_bp.route('/adv/settings', methods=['POST'])
@login_required
def adv_settings():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().dump_all_settings(serial))
# ── Advanced: Network ────────────────────────────────────────────
@android_exploit_bp.route('/adv/network-info', methods=['POST'])
@login_required
def adv_network_info():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().get_network_info(serial))
@android_exploit_bp.route('/adv/proxy-set', methods=['POST'])
@login_required
def adv_proxy_set():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
host = data.get('host', '').strip()
port = data.get('port', '').strip()
if not host or not port:
return jsonify({'error': 'Missing host or port'})
return jsonify(_get_mgr().set_proxy(serial, host, port))
@android_exploit_bp.route('/adv/proxy-clear', methods=['POST'])
@login_required
def adv_proxy_clear():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().clear_proxy(serial))
@android_exploit_bp.route('/adv/wifi-scan', methods=['POST'])
@login_required
def adv_wifi_scan():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().wifi_scan(serial))
@android_exploit_bp.route('/adv/wifi-connect', methods=['POST'])
@login_required
def adv_wifi_connect():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
return jsonify(_get_mgr().wifi_connect(serial, data.get('ssid',''), data.get('password','')))
@android_exploit_bp.route('/adv/adb-wifi', methods=['POST'])
@login_required
def adv_adb_wifi():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
return jsonify(_get_mgr().enable_adb_wifi(serial, int(data.get('port', 5555))))
@android_exploit_bp.route('/adv/capture-traffic', methods=['POST'])
@login_required
def adv_capture():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
return jsonify(_get_mgr().capture_traffic(serial,
interface=data.get('interface', 'any'),
duration=int(data.get('duration', 30)),
pcap_filter=data.get('filter', '')))
# ── Advanced: System ─────────────────────────────────────────────
@android_exploit_bp.route('/adv/selinux', methods=['POST'])
@login_required
def adv_selinux():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
return jsonify(_get_mgr().set_selinux(serial, data.get('mode', 'permissive')))
@android_exploit_bp.route('/adv/remount', methods=['POST'])
@login_required
def adv_remount():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().remount_system(serial))
@android_exploit_bp.route('/adv/logcat-sensitive', methods=['POST'])
@login_required
def adv_logcat():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
return jsonify(_get_mgr().logcat_sensitive(serial, int(data.get('duration', 10))))
@android_exploit_bp.route('/adv/processes', methods=['POST'])
@login_required
def adv_processes():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().get_running_processes(serial))
@android_exploit_bp.route('/adv/ports', methods=['POST'])
@login_required
def adv_ports():
serial, err = _get_serial()
if err:
return err
return jsonify(_get_mgr().get_open_ports(serial))
@android_exploit_bp.route('/adv/modify-setting', methods=['POST'])
@login_required
def adv_modify_setting():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
ns = data.get('namespace', '').strip()
key = data.get('key', '').strip()
value = data.get('value', '').strip()
if not ns or not key:
return jsonify({'error': 'Missing namespace or key'})
return jsonify(_get_mgr().modify_setting(serial, ns, key, value))
@android_exploit_bp.route('/adv/app-disable', methods=['POST'])
@login_required
def adv_app_disable():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
package = data.get('package', '').strip()
if not package:
return jsonify({'error': 'No package provided'})
return jsonify(_get_mgr().disable_app(serial, package))
@android_exploit_bp.route('/adv/app-enable', methods=['POST'])
@login_required
def adv_app_enable():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
package = data.get('package', '').strip()
if not package:
return jsonify({'error': 'No package provided'})
return jsonify(_get_mgr().enable_app(serial, package))
@android_exploit_bp.route('/adv/app-clear', methods=['POST'])
@login_required
def adv_app_clear():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
package = data.get('package', '').strip()
if not package:
return jsonify({'error': 'No package provided'})
return jsonify(_get_mgr().clear_app_data(serial, package))
@android_exploit_bp.route('/adv/app-launch', methods=['POST'])
@login_required
def adv_app_launch():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
package = data.get('package', '').strip()
if not package:
return jsonify({'error': 'No package provided'})
return jsonify(_get_mgr().launch_app(serial, package))
@android_exploit_bp.route('/adv/content-query', methods=['POST'])
@login_required
def adv_content_query():
serial, err = _get_serial()
if err:
return err
data = request.get_json(silent=True) or {}
uri = data.get('uri', '').strip()
if not uri:
return jsonify({'error': 'No URI provided'})
return jsonify(_get_mgr().content_query(serial, uri,
projection=data.get('projection', ''), where=data.get('where', '')))
# ── WebUSB Direct Mode: Command Relay ────────────────────────────────
@android_exploit_bp.route('/cmd', methods=['POST'])
@login_required
def get_direct_commands():
"""Return ADB shell command(s) for an operation without executing them.
Used by WebUSB Direct mode: browser executes via HWDirect.adbShell().
Returns one of:
{commands: ['cmd1', 'cmd2', ...]} shell operations
{pullPath: '/device/path'} file pull operations
{error: 'message'} unsupported operation
"""
data = request.get_json(silent=True) or {}
op = data.get('op', '')
params = data.get('params', {})
result = _get_mgr().get_commands_for_op(op, params)
return jsonify(result)
@android_exploit_bp.route('/parse', methods=['POST'])
@login_required
def parse_direct_output():
"""Parse raw ADB shell output from WebUSB Direct mode execution.
Takes the raw text output and returns the same structured JSON
that the normal server-mode endpoint would return.
"""
data = request.get_json(silent=True) or {}
op = data.get('op', '')
params = data.get('params', {})
raw = data.get('raw', '')
result = _get_mgr().parse_op_output(op, params, raw)
return jsonify(result)