├── ADBuster.py ├── README.md ├── Readme-EN.MD ├── __pycache__ ├── adb_file_explorer.cpython-313.pyc └── terminal.cpython-313.pyc ├── requirements.txt ├── scrcpy ├── AdbWinApi.dll ├── AdbWinUsbApi.dll ├── SDL2.dll ├── adb.exe ├── avcodec-61.dll ├── avformat-61.dll ├── avutil-59.dll ├── icon.png ├── libusb-1.0.dll ├── open_a_terminal_here.bat ├── scrcpy-console.bat ├── scrcpy-noconsole.vbs ├── scrcpy-server ├── scrcpy.exe └── swresample-5.dll └── scripts ├── __pycache__ ├── adb_fastboot_partitions.cpython-313.pyc ├── adb_file_explorer.cpython-313.pyc ├── adb_sideload.cpython-313.pyc └── terminal.cpython-313.pyc ├── adb_file_explorer.py ├── adb_sideload.py └── terminal.py /ADBuster.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import time 3 | import os 4 | from ppadb.client import Client as AdbClient 5 | from tkinter import Tk 6 | from tkinter import filedialog 7 | import curses 8 | 9 | # Intentar importar módulos con depuración detallada 10 | file_explorer_main = None 11 | terminal_main = None 12 | adb_sideload_main = None 13 | 14 | try: 15 | from scripts.adb_file_explorer import main as file_explorer_main 16 | print("[+] Módulo adb_file_explorer importado correctamente.") 17 | except ImportError as e: 18 | print(f"[!] Error importando adb_file_explorer: {e}") 19 | except Exception as e: 20 | print(f"[!] Error inesperado al importar adb_file_explorer: {e}") 21 | 22 | try: 23 | from scripts.terminal import main as terminal_main 24 | print("[+] Módulo terminal importado correctamente.") 25 | except ImportError as e: 26 | print(f"[!] Error importando terminal: {e}") 27 | except Exception as e: 28 | print(f"[!] Error inesperado al importar terminal: {e}") 29 | 30 | try: 31 | from scripts.adb_sideload import main as adb_sideload_main 32 | print("[+] Módulo adb_sideload importado correctamente.") 33 | except ImportError as e: 34 | print(f"[!] Error importando adb_sideload: {e}") 35 | except Exception as e: 36 | print(f"[!] Error inesperado al importar adb_sideload: {e}") 37 | 38 | # Diccionario de textos 39 | TEXTS = { 40 | "es": { 41 | # Language Selection 42 | "select_language": "Seleccione idioma / Select language:", 43 | "language_option_1": "Español", 44 | "language_option_2": "English", 45 | "select_language_prompt": "Seleccione 1 o 2 y Enter: ", 46 | "invalid_language": "¡Opción no válida! Inténtelo de nuevo.", 47 | # General 48 | "error_unexpected": "[-] Error inesperado: {error}", 49 | "interrupted_keyboard": "\n[-] Interrumpido por teclado. Saliendo...", 50 | "press_enter_continue": "Presione Enter para continuar...", 51 | "invalid_option": "[-] Opción no válida. Intente de nuevo.", 52 | "exiting": "Saliendo...", 53 | "no_device_selected": "[-] No se seleccionó ningún dispositivo.", 54 | "no_wifi_device_connected": "[-] No hay dispositivo conectado por Wi-Fi.", 55 | # ADB Server 56 | "starting_adb": "[*] Iniciando servidor ADB...", 57 | "adb_started": "[+] Servidor ADB iniciado.\n", 58 | "stopping_adb": "[*] Deteniendo servidor ADB...", 59 | "adb_stopped": "[+] Servidor ADB detenido.\n", 60 | # Device Connection & Selection 61 | "no_usb_device": "[-] No se detectó dispositivo USB.", 62 | "getting_ip_error": "[-] No se pudo obtener la IP.", 63 | "connecting_wifi": "[*] Conectando por Wi-Fi a {ip}:{port}...", 64 | "connection_result": "{result}", 65 | "disconnecting_wifi": "[*] Desconectando dispositivos Wi-Fi...", 66 | "disconnected_all_wifi": "[+] Todos los dispositivos Wi-Fi desconectados.", 67 | "listing_devices": "[*] Dispositivos conectados:", 68 | "device_serial": " - {serial}", 69 | "no_devices_connected": "[-] No hay dispositivos conectados.", 70 | "available_devices": "Dispositivos disponibles:", 71 | "device_list_item": "{index}. {serial}", 72 | "select_device_prompt": "Seleccione un dispositivo: ", 73 | "invalid_input_number": "[-] Entrada inválida. Por favor, ingrese un número.", 74 | "error_selecting_device": "[-] Error al seleccionar el dispositivo: {error}", 75 | "no_usb_prompt_ip": "[-] No se detectaron dispositivos USB. ¿Desea conectarse por IP?", 76 | "prompt_yes_no": "Ingrese 'si' o 'no': ", 77 | "enter_ip_prompt": "Ingrese la dirección IP del dispositivo: ", 78 | "connecting_ip_error": "[-] Error al conectar por IP: {error}", 79 | "connect_ip_fail": "[-] No se pudo conectar al dispositivo con la IP proporcionada.", 80 | "connected_to_ip": "[+] Conectado a {serial}", 81 | "using_wifi_device": "[*] Usando dispositivo Wi-Fi conectado.", 82 | # Commands & Actions 83 | "executing_command_error": "[-] Error al ejecutar comando: {error}", 84 | "command_result": "[+] Resultado:\n{result}\n", 85 | "enter_command_prompt": "Escriba el comando a ejecutar: ", 86 | "rebooting_device": "[*] Reiniciando dispositivo...", 87 | "rebooted_device": "[+] Dispositivo reiniciado en modo '{mode}'.", 88 | "reboot_error": "[-] Error al reiniciar: {error}", 89 | "reboot_mode_normal": "normal", 90 | "checking_wifi_connection": "[*] Verificando conexión Wi-Fi...", 91 | "wifi_check_ok": "[+] Dispositivo conectado por Wi-Fi.", 92 | "wifi_check_fail": "[-] El dispositivo no está conectado por Wi-Fi.", 93 | "installing_apk": "[*] Instalando APK: {path}", 94 | "apk_install_success": "[+] APK instalado correctamente.", 95 | "apk_install_error": "[-] Error al instalar APK: {error}", 96 | "no_apk_selected": "[-] No se seleccionó ningún archivo APK.", 97 | "select_apk_title": "Seleccionar archivo APK", 98 | "scrcpy_not_found": "[-] scrcpy.exe no encontrado en la carpeta ./scrcpy/", 99 | "scrcpy_extract_prompt": "Asegúrese de haber extraído el archivo ZIP de scrcpy dentro del directorio del proyecto.", 100 | "launching_scrcpy": "[*] Lanzando scrcpy para {serial}...", 101 | "scrcpy_launched": "[+] scrcpy lanzado para {serial}", 102 | "scrcpy_error": "[-] Error al ejecutar scrcpy: {error}", 103 | "getting_device_info": "[*] Obteniendo información del dispositivo...", 104 | "device_info_model": "Modelo: {model}", 105 | "device_info_android_version": "Versión de Android: {version}", 106 | "device_info_manufacturer": "Fabricante: {manufacturer}", 107 | "device_info_error": "[-] Error al obtener información del dispositivo: {error}", 108 | "listing_apps": "[*] Listando aplicaciones instaladas...", 109 | "list_apps_error": "[-] Error al listar aplicaciones: {error}", 110 | "app_package_name": "{package}", 111 | "opening_file_explorer": "[*] Abriendo administrador de archivos... ¡Prepárate para explorar!", 112 | "file_explorer_closed": "[+] Administrador de archivos cerrado.", 113 | "file_explorer_error": "[-] Error al abrir el administrador de archivos: {error}", 114 | "starting_sideload": "[*] Iniciando modo sideload...", 115 | "sideload_closed": "[+] Modo sideload cerrado.", 116 | "sideload_error": "[-] Error en el modo sideload: {error}", 117 | "no_active_device_sideload": "[*] No hay dispositivo activo. Por favor, selecciona uno para sideload.", 118 | "sideload_module_error": "[!] El módulo de sideload no se cargó correctamente.", 119 | "opening_fastboot_partitions": "[*] Abriendo explorador de particiones Fastboot...", 120 | "fastboot_partitions_closed": "[+] Explorador de particiones Fastboot cerrado.", 121 | "fastboot_partitions_error": "[-] Error al abrir explorador de particiones: {error}", 122 | "no_active_device_fastboot": "[*] No hay dispositivo activo. Por favor, selecciona uno para Fastboot.", 123 | "fastboot_module_error": "[!] El módulo de particiones Fastboot no se cargó correctamente.", 124 | # Menus 125 | "separator_line": "===================================", 126 | "main_menu_title": " ADBuster - Menú", 127 | "menu_1_start_adb": "1. Iniciar servidor ADB", 128 | "menu_2_stop_adb": "2. Detener servidor ADB", 129 | "menu_3_reboot": "3. Menú de reinicio", 130 | "menu_4_bypass": "4. Bypass USB/WIFI", 131 | "menu_5_disconnect": "5. Desconectar dispositivos por Wi-Fi", 132 | "menu_6_list_devices": "6. Listar dispositivos conectados", 133 | "menu_7_run_command": "7. Abrir linea de comandos", 134 | "menu_8_install_apk": "8. Instalar APK", 135 | "menu_9_open_scrcpy": "9. Abrir scrcpy", 136 | "menu_10_device_info": "10. Obtener información del dispositivo", 137 | "menu_11_list_apps": "11. Listar aplicaciones instaladas", 138 | "menu_12_connect_ip": "12. Conectar por IP", 139 | "menu_13_file_explorer": "13. Abrir administrador de archivos", 140 | "menu_14_sideload": "14. Adb Sideload", 141 | "menu_15_exit": "15. Salir", 142 | "submenu_reboot_separator": "-----------------------------------", 143 | "submenu_reboot_title": " Submenú de Reinicio", 144 | "submenu_reboot_1_normal": "1. Reiniciar", 145 | "submenu_reboot_2_recovery": "2. Reiniciar a Recovery", 146 | "submenu_reboot_3_fastboot": "3. Reiniciar a Fastboot", 147 | "submenu_reboot_4_edl": "4. Reiniciar a EDL", 148 | "submenu_reboot_5_back": "5. Volver al menú principal", 149 | "select_option_prompt": "Seleccione una opción: ", 150 | "select_reboot_option_prompt": "Seleccione una opción de reinicio: " 151 | }, 152 | "en": { 153 | # Language Selection 154 | "select_language": "Select language / Seleccione idioma:", 155 | "language_option_1": "Spanish", 156 | "language_option_2": "English", 157 | "select_language_prompt": "Select 1 or 2 and press Enter: ", 158 | "invalid_language": "Invalid option! Please try again.", 159 | # General 160 | "error_unexpected": "[-] Unexpected error: {error}", 161 | "interrupted_keyboard": "\n[-] Keyboard interrupt. Exiting...", 162 | "press_enter_continue": "Press Enter to continue...", 163 | "invalid_option": "[-] Invalid option. Try again.", 164 | "exiting": "Exiting...", 165 | "no_device_selected": "[-] No device selected.", 166 | "no_wifi_device_connected": "[-] No device connected via Wi-Fi.", 167 | # ADB Server 168 | "starting_adb": "[*] Starting ADB server...", 169 | "adb_started": "[+] ADB server started.\n", 170 | "stopping_adb": "[*] Stopping ADB server...", 171 | "adb_stopped": "[+] ADB server stopped.\n", 172 | # Device Connection & Selection 173 | "no_usb_device": "[-] No USB device detected.", 174 | "getting_ip_error": "[-] Could not get IP address.", 175 | "connecting_wifi": "[*] Connecting via Wi-Fi to {ip}:{port}...", 176 | "connection_result": "{result}", 177 | "disconnecting_wifi": "[*] Disconnecting Wi-Fi devices...", 178 | "disconnected_all_wifi": "[+] All Wi-Fi devices disconnected.", 179 | "listing_devices": "[*] Connected devices:", 180 | "device_serial": " - {serial}", 181 | "no_devices_connected": "[-] No devices connected.", 182 | "available_devices": "Available devices:", 183 | "device_list_item": "{index}. {serial}", 184 | "select_device_prompt": "Select a device: ", 185 | "invalid_input_number": "[-] Invalid input. Please enter a number.", 186 | "error_selecting_device": "[-] Error selecting device: {error}", 187 | "no_usb_prompt_ip": "[-] No USB devices detected. Connect via IP?", 188 | "prompt_yes_no": "Enter 'yes' or 'no': ", 189 | "enter_ip_prompt": "Enter the device IP address: ", 190 | "connecting_ip_error": "[-] Error connecting via IP: {error}", 191 | "connect_ip_fail": "[-] Could not connect to the device with the provided IP.", 192 | "connected_to_ip": "[+] Connected to {serial}", 193 | "using_wifi_device": "[*] Using connected Wi-Fi device.", 194 | # Commands & Actions 195 | "executing_command_error": "[-] Error executing command: {error}", 196 | "command_result": "[+] Result:\n{result}\n", 197 | "enter_command_prompt": "Enter the command to execute: ", 198 | "rebooting_device": "[*] Rebooting device...", 199 | "rebooted_device": "[+] Device rebooted into '{mode}' mode.", 200 | "reboot_error": "[-] Error rebooting: {error}", 201 | "reboot_mode_normal": "normal", 202 | "checking_wifi_connection": "[*] Checking Wi-Fi connection...", 203 | "wifi_check_ok": "[+] Device connected via Wi-Fi.", 204 | "wifi_check_fail": "[-] Device is not connected via Wi-Fi.", 205 | "installing_apk": "[*] Installing APK: {path}", 206 | "apk_install_success": "[+] APK installed successfully.", 207 | "apk_install_error": "[-] Error installing APK: {error}", 208 | "no_apk_selected": "[-] No APK file selected.", 209 | "select_apk_title": "Select APK File", 210 | "scrcpy_not_found": "[-] scrcpy.exe not found in ./scrcpy/ folder.", 211 | "scrcpy_extract_prompt": "Make sure you have extracted the scrcpy ZIP file into the project directory.", 212 | "launching_scrcpy": "[*] Launching scrcpy for {serial}...", 213 | "scrcpy_launched": "[+] scrcpy launched for {serial}", 214 | "scrcpy_error": "[-] Error executing scrcpy: {error}", 215 | "getting_device_info": "[*] Getting device information...", 216 | "device_info_model": "Model: {model}", 217 | "device_info_android_version": "Android Version: {version}", 218 | "device_info_manufacturer": "Manufacturer: {manufacturer}", 219 | "device_info_error": "[-] Error getting device info: {error}", 220 | "listing_apps": "[*] Listing installed applications...", 221 | "list_apps_error": "[-] Error listing applications: {error}", 222 | "app_package_name": "{package}", 223 | "opening_file_explorer": "[*] Opening file explorer... Get ready to explore!", 224 | "file_explorer_closed": "[+] File explorer closed.", 225 | "file_explorer_error": "[-] Error opening file explorer: {error}", 226 | "starting_sideload": "[*] Starting sideload mode...", 227 | "sideload_closed": "[+] Sideload mode closed.", 228 | "sideload_error": "[-] Error in sideload mode: {error}", 229 | "no_active_device_sideload": "[*] No active device. Please select one for sideload.", 230 | "sideload_module_error": "[!] Sideload module not loaded correctly.", 231 | "opening_fastboot_partitions": "[*] Opening Fastboot partitions explorer...", 232 | "fastboot_partitions_closed": "[+] Fastboot partitions explorer closed.", 233 | "fastboot_partitions_error": "[-] Error opening partitions explorer: {error}", 234 | "no_active_device_fastboot": "[*] No active device. Please select one for Fastboot.", 235 | "fastboot_module_error": "[!] Fastboot partitions module not loaded correctly.", 236 | # Menus 237 | "separator_line": "===================================", 238 | "main_menu_title": " ADBuster - Menu", 239 | "menu_1_start_adb": "1. Start ADB server", 240 | "menu_2_stop_adb": "2. Stop ADB server", 241 | "menu_3_reboot": "3. Reboot menu", 242 | "menu_4_bypass": "4. Bypass USB/WIFI", 243 | "menu_5_disconnect": "5. Disconnect Wi-Fi devices", 244 | "menu_6_list_devices": "6. List connected devices", 245 | "menu_7_run_command": "7. Open command line", 246 | "menu_8_install_apk": "8. Install APK", 247 | "menu_9_open_scrcpy": "9. Open scrcpy", 248 | "menu_10_device_info": "10. Get device information", 249 | "menu_11_list_apps": "11. List installed applications", 250 | "menu_12_connect_ip": "12. Connect via IP", 251 | "menu_13_file_explorer": "13. Open file manager", 252 | "menu_14_sideload": "14. Adb Sideload", 253 | "menu_15_exit": "16. Exit", 254 | "select_option_prompt": "Select an option: ", 255 | "submenu_reboot_separator": "-----------------------------------", 256 | "submenu_reboot_title": " Reboot Submenu", 257 | "submenu_reboot_1_normal": "1. Reboot", 258 | "submenu_reboot_2_recovery": "2. Re Techniques to Recovery", 259 | "submenu_reboot_3_fastboot": "3. Reboot to Fastboot", 260 | "submenu_reboot_4_edl": "4. Reboot to EDL", 261 | "submenu_reboot_5_back": "5. Back to main menu", 262 | "select_reboot_option_prompt": "Select a reboot option: " 263 | } 264 | } 265 | 266 | # --- Language Selection Function --- 267 | def seleccionar_idioma(texts): 268 | """ Asks the user to select a language """ 269 | while True: 270 | limpiar_pantalla() 271 | print(texts["es"]["select_language"]) 272 | print(f"1. {texts['es']['language_option_1']}") 273 | print(f"2. {texts['en']['language_option_2']}") 274 | choice = input(texts["es"]["select_language_prompt"]).strip() 275 | if choice == '1': 276 | return 'es' 277 | elif choice == '2': 278 | return 'en' 279 | else: 280 | print(texts["es"]["invalid_language"]) 281 | time.sleep(2) 282 | 283 | # --- System Utility --- 284 | def limpiar_pantalla(): 285 | os.system("cls" if os.name == "nt" else "clear") 286 | 287 | # --- ADB Functions (Modified for Language Support) --- 288 | def iniciar_adb_server(language, texts): 289 | print(texts[language]["starting_adb"]) 290 | subprocess.run(["adb", "start-server"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 291 | print(texts[language]["adb_started"]) 292 | time.sleep(2) 293 | limpiar_pantalla() 294 | 295 | def detener_adb_server(language, texts): 296 | print(texts[language]["stopping_adb"]) 297 | subprocess.run(["adb", "kill-server"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 298 | print(texts[language]["adb_stopped"]) 299 | time.sleep(2) 300 | limpiar_pantalla() 301 | 302 | def obtener_dispositivo_usb(): 303 | resultado = subprocess.run(["adb", "devices"], capture_output=True, text=True, encoding='utf-8', errors='ignore') 304 | dispositivos = resultado.stdout.strip().splitlines()[1:] 305 | for linea in dispositivos: 306 | if "\tdevice" in linea: 307 | return linea.split("\t")[0] 308 | return None 309 | 310 | def obtener_ip_dispositivo(serial): 311 | try: 312 | resultado = subprocess.run( 313 | ["adb", "-s", serial, "shell", "ip -f inet addr show wlan0"], 314 | capture_output=True, 315 | text=True, 316 | timeout=10, # Add timeout 317 | encoding='utf-8', errors='ignore' 318 | ) 319 | for linea in resultado.stdout.strip().splitlines(): 320 | if "inet " in linea: 321 | parts = linea.strip().split(" ") 322 | if len(parts) > 1: 323 | ip_part = parts[1] 324 | return ip_part.split("/")[0] 325 | except (subprocess.TimeoutExpired, FileNotFoundError): 326 | return None 327 | return None 328 | 329 | def activar_tcpip(serial, puerto=5555): 330 | subprocess.run(["adb", "-s", serial, "tcpip", str(puerto)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 331 | 332 | def conectar_por_wifi(ip, language, texts, puerto=5555): 333 | print(texts[language]["connecting_wifi"].format(ip=ip, port=puerto)) 334 | resultado = subprocess.run(["adb", "connect", f"{ip}:{puerto}"], capture_output=True, text=True, encoding='utf-8', errors='ignore') 335 | output = resultado.stdout.strip() 336 | if not output: 337 | output = resultado.stderr.strip() 338 | print(texts[language]["connection_result"].format(result=output)) 339 | time.sleep(3) 340 | 341 | def desconectar_dispositivos_wifi(language, texts): 342 | print(texts[language]["disconnecting_wifi"]) 343 | subprocess.run(["adb", "disconnect"], capture_output=True, text=True, encoding='utf-8', errors='ignore') 344 | print(texts[language]["disconnected_all_wifi"]) 345 | time.sleep(3) 346 | limpiar_pantalla() 347 | 348 | def conectar_con_ppadb(): 349 | try: 350 | client = AdbClient(host="127.0.0.1", port=5037) 351 | dispositivos = client.devices() 352 | return dispositivos 353 | except RuntimeError as e: 354 | return [] 355 | 356 | def ejecutar_comando_en_dispositivo(dispositivo, comando, language, texts): 357 | try: 358 | resultado = dispositivo.shell(comando) 359 | print(texts[language]["command_result"].format(result=resultado.strip())) 360 | except Exception as e: 361 | print(texts[language]["executing_command_error"].format(error=e)) 362 | time.sleep(3) 363 | # limpiar_pantalla() 364 | 365 | def reiniciar_dispositivo(dispositivo, language, texts, modo=""): 366 | print(texts[language]["rebooting_device"]) 367 | try: 368 | comando = f"reboot {modo}".strip() 369 | dispositivo.shell(comando) 370 | modo_display = modo if modo else texts[language]["reboot_mode_normal"] 371 | print(texts[language]["rebooted_device"].format(mode=modo_display)) 372 | except Exception as e: 373 | print(texts[language]["reboot_error"].format(error=e)) 374 | time.sleep(3) 375 | limpiar_pantalla() 376 | 377 | def verificar_conexion_wifi(dispositivo, language, texts): 378 | # print(texts[language]["checking_wifi_connection"]) 379 | try: 380 | resultado = dispositivo.shell("echo CONECTADO") 381 | is_connected = "CONECTADO" in resultado 382 | # if is_connected: 383 | # print(texts[language]["wifi_check_ok"]) 384 | # else: 385 | # print(texts[language]["wifi_check_fail"]) # Optional 386 | return is_connected 387 | except Exception: 388 | # print(texts[language]["wifi_check_fail"]) # Optional 389 | return False 390 | 391 | def instalar_apk(dispositivo, language, texts): 392 | if not dispositivo: 393 | print(texts[language]["no_device_selected"]) 394 | time.sleep(3) 395 | limpiar_pantalla() 396 | return 397 | 398 | try: 399 | Tk().withdraw() 400 | apk_path = filedialog.askopenfilename( 401 | title=texts[language]["select_apk_title"], 402 | filetypes=[("APK Files", "*.apk")] 403 | ) 404 | if not apk_path: 405 | print(texts[language]["no_apk_selected"]) 406 | time.sleep(3) 407 | limpiar_pantalla() 408 | return 409 | print(texts[language]["installing_apk"].format(path=os.path.basename(apk_path))) 410 | resultado = dispositivo.install(apk_path) 411 | if resultado: 412 | print(texts[language]["apk_install_success"]) 413 | else: 414 | print(texts[language]["apk_install_error"].format(error="Installation failed (check device screen for details)")) 415 | except Exception as e: 416 | print(texts[language]["apk_install_error"].format(error=e)) 417 | time.sleep(3) 418 | limpiar_pantalla() 419 | 420 | def obtener_dispositivos(language, texts): 421 | devices = conectar_con_ppadb() 422 | if not devices: 423 | pass 424 | return devices 425 | 426 | def seleccionar_dispositivo(language, texts): 427 | try: 428 | dispositivos = obtener_dispositivos(language, texts) 429 | if dispositivos: 430 | print(texts[language]["available_devices"]) 431 | for i, d in enumerate(dispositivos): 432 | print(texts[language]["device_list_item"].format(index=i + 1, serial=d.serial)) 433 | while True: 434 | try: 435 | indice_str = input(texts[language]["select_device_prompt"]).strip() 436 | if not indice_str: 437 | return None 438 | indice = int(indice_str) - 1 439 | if 0 <= indice < len(dispositivos): 440 | return dispositivos[indice] 441 | else: 442 | print(texts[language]["invalid_option"]) 443 | except ValueError: 444 | print(texts[language]["invalid_input_number"]) 445 | else: 446 | print(texts[language]["no_usb_prompt_ip"]) 447 | respuesta = input(texts[language]["prompt_yes_no"]).strip().lower() 448 | if respuesta == "yes" or respuesta == "si" or respuesta == "s": 449 | ip = input(texts[language]["enter_ip_prompt"]).strip() 450 | if not ip: return None 451 | try: 452 | conectar_por_wifi(ip, language, texts) 453 | dispositivos_actualizados = obtener_dispositivos(language, texts) 454 | for d in dispositivos_actualizados: 455 | if ip in d.serial: 456 | print(texts[language]["connected_to_ip"].format(serial=d.serial)) 457 | time.sleep(2) 458 | return d 459 | print(texts[language]["connect_ip_fail"]) 460 | time.sleep(3) 461 | return None 462 | except Exception as e: 463 | print(texts[language]["connecting_ip_error"].format(error=e)) 464 | time.sleep(3) 465 | return None 466 | else: 467 | return None 468 | except Exception as e: 469 | print(texts[language]["error_selecting_device"].format(error=e)) 470 | time.sleep(3) 471 | return None 472 | 473 | def abrir_scrcpy(dispositivo, language, texts): 474 | base_path = os.path.dirname(os.path.abspath(__file__)) 475 | ruta_scrcpy = os.path.join(base_path, "scrcpy", "scrcpy.exe") 476 | ruta_scrcpylin = os.path.join(base_path, "scrcpy", "scrcpy") # Soporte Linux is coming xD 477 | 478 | if not os.path.exists(ruta_scrcpy): 479 | print(texts[language]["scrcpy_not_found"]) 480 | print(texts[language]["scrcpy_extract_prompt"]) 481 | time.sleep(4) 482 | return 483 | 484 | print(texts[language]["launching_scrcpy"].format(serial=dispositivo.serial)) 485 | try: 486 | subprocess.Popen([ruta_scrcpy, "-s", dispositivo.serial]) 487 | print(texts[language]["scrcpy_launched"].format(serial=dispositivo.serial)) 488 | time.sleep(2) 489 | except Exception as e: 490 | print(texts[language]["scrcpy_error"].format(error=e)) 491 | time.sleep(3) 492 | 493 | def abrir_scrcpy_linux(dispositivo, language, texts): 494 | base_path = os.path.dirname(os.path.abspath(__file__)) 495 | ruta_scrcpy = os.path.join(base_path, "scrcpy", "scrcpy") 496 | 497 | if not os.path.exists(ruta_scrcpy): 498 | print(texts[language]["scrcpy_not_found"]) 499 | print(texts[language]["scrcpy_extract_prompt"]) 500 | time.sleep(4) 501 | return 502 | 503 | print(texts[language]["launching_scrcpy"].format(serial=dispositivo.serial)) 504 | try: 505 | subprocess.Popen([ruta_scrcpy, "-s", dispositivo.serial]) 506 | print(texts[language]["scrcpy_launched"].format(serial=dispositivo.serial)) 507 | time.sleep(2) 508 | except Exception as e: 509 | print(texts[language]["scrcpy_error"].format(error=e)) 510 | time.sleep(3) 511 | 512 | def obtener_info_dispositivo(dispositivo, language, texts): 513 | print(texts[language]["getting_device_info"]) 514 | try: 515 | modelo = dispositivo.shell("getprop ro.product.model").strip() 516 | version_android = dispositivo.shell("getprop ro.build.version.release").strip() 517 | fabricante = dispositivo.shell("getprop ro.product.manufacturer").strip() 518 | print(texts[language]["device_info_model"].format(model=modelo)) 519 | print(texts[language]["device_info_android_version"].format(version=version_android)) 520 | print(texts[language]["device_info_manufacturer"].format(manufacturer=fabricante)) 521 | except Exception as e: 522 | print(texts[language]["device_info_error"].format(error=e)) 523 | input(texts[language]["press_enter_continue"]) # Wait for user 524 | limpiar_pantalla() 525 | 526 | def listar_aplicaciones(dispositivo, language, texts): 527 | print(texts[language]["listing_apps"]) 528 | try: 529 | aplicaciones = dispositivo.shell("pm list packages -3").splitlines() 530 | if not aplicaciones: 531 | aplicaciones = dispositivo.shell("pm list packages").splitlines() 532 | 533 | for app in sorted(aplicaciones): 534 | package_name = app.replace("package:", "").strip() 535 | if package_name: 536 | print(texts[language]["app_package_name"].format(package=package_name)) 537 | except Exception as e: 538 | print(texts[language]["list_apps_error"].format(error=e)) 539 | 540 | input(texts[language]["press_enter_continue"]) 541 | limpiar_pantalla() 542 | 543 | def conectar_por_ip(language, texts): 544 | ip = input(texts[language]["enter_ip_prompt"]).strip() 545 | if not ip: return None 546 | try: 547 | conectar_por_wifi(ip, language, texts) 548 | dispositivos = obtener_dispositivos(language, texts) 549 | for d in dispositivos: 550 | if ip in d.serial: 551 | print(texts[language]["connected_to_ip"].format(serial=d.serial)) 552 | time.sleep(2) 553 | limpiar_pantalla() 554 | return d 555 | print(texts[language]["connect_ip_fail"]) 556 | time.sleep(3) 557 | limpiar_pantalla() 558 | return None 559 | except Exception as e: 560 | print(texts[language]["connecting_ip_error"].format(error=e)) 561 | time.sleep(3) 562 | limpiar_pantalla() 563 | return None 564 | 565 | # --- Menus (Modified for Language Support) --- 566 | def mostrar_menu(language, texts): 567 | print(texts[language]["separator_line"]) 568 | print(texts[language]["main_menu_title"]) 569 | print(texts[language]["separator_line"]) 570 | print(texts[language]["menu_1_start_adb"]) 571 | print(texts[language]["menu_2_stop_adb"]) 572 | print(texts[language]["menu_3_reboot"]) 573 | print(texts[language]["menu_4_bypass"]) 574 | print(texts[language]["menu_5_disconnect"]) 575 | print(texts[language]["menu_6_list_devices"]) 576 | print(texts[language]["menu_7_run_command"]) 577 | print(texts[language]["menu_8_install_apk"]) 578 | print(texts[language]["menu_9_open_scrcpy"]) 579 | print(texts[language]["menu_10_device_info"]) 580 | print(texts[language]["menu_11_list_apps"]) 581 | print(texts[language]["menu_12_connect_ip"]) 582 | if file_explorer_main: 583 | print(texts[language]["menu_13_file_explorer"]) 584 | if adb_sideload_main: 585 | print(texts[language]["menu_14_sideload"]) 586 | print(texts[language]["menu_15_exit"]) 587 | print(texts[language]["separator_line"]) 588 | 589 | def mostrar_submenu_reinicio(language, texts): 590 | print(texts[language]["submenu_reboot_separator"]) 591 | print(texts[language]["submenu_reboot_title"]) 592 | print(texts[language]["submenu_reboot_separator"]) 593 | print(texts[language]["submenu_reboot_1_normal"]) 594 | print(texts[language]["submenu_reboot_2_recovery"]) 595 | print(texts[language]["submenu_reboot_3_fastboot"]) 596 | print(texts[language]["submenu_reboot_4_edl"]) 597 | print(texts[language]["submenu_reboot_5_back"]) 598 | print(texts[language]["submenu_reboot_separator"]) 599 | 600 | # --- Main CLI Loop (Modified for Language Support) --- 601 | def cli_adb(language, texts): 602 | dispositivo_activo = None # Renamed for clarity (can be USB or Wi-Fi) 603 | 604 | while True: 605 | limpiar_pantalla() 606 | # Display currently active device if any 607 | if dispositivo_activo: 608 | print(f"[{texts[language]['available_devices']} {dispositivo_activo.serial}]") # Quick info 609 | else: 610 | print(f"[{texts[language]['no_devices_connected']}]") 611 | 612 | mostrar_menu(language, texts) 613 | opcion = input(texts[language]["select_option_prompt"]).strip() 614 | 615 | if opcion == "1": 616 | iniciar_adb_server(language, texts) 617 | # Try to auto-select a device after starting server 618 | devices = obtener_dispositivos(language, texts) 619 | if devices: 620 | dispositivo_activo = devices[0] # Select the first one automatically 621 | 622 | elif opcion == "2": 623 | detener_adb_server(language, texts) 624 | dispositivo_activo = None # Clear active device 625 | 626 | elif opcion == "3": # Reinicio 627 | if not dispositivo_activo: 628 | print(texts[language]["no_device_selected"]) # Use general message 629 | time.sleep(3) 630 | continue # No need for screen clear here, loop will do it 631 | 632 | while True: 633 | limpiar_pantalla() 634 | mostrar_submenu_reinicio(language, texts) 635 | subopcion = input(texts[language]["select_reboot_option_prompt"]).strip() 636 | if subopcion == "1": 637 | reiniciar_dispositivo(dispositivo_activo, language, texts) 638 | dispositivo_activo = None 639 | break 640 | elif subopcion == "2": 641 | reiniciar_dispositivo(dispositivo_activo, language, texts, "recovery") 642 | dispositivo_activo = None 643 | break 644 | elif subopcion == "3": 645 | reiniciar_dispositivo(dispositivo_activo, language, texts, "bootloader") 646 | dispositivo_activo = None 647 | break 648 | elif subopcion == "4": 649 | reiniciar_dispositivo(dispositivo_activo, language, texts, "edl") 650 | dispositivo_activo = None 651 | break 652 | elif subopcion == "5": 653 | break # Go back to main menu 654 | else: 655 | print(texts[language]["invalid_option"]) 656 | time.sleep(2) 657 | 658 | elif opcion == "4": # Bypass USB/WIFI 659 | serial_usb = obtener_dispositivo_usb() 660 | if not serial_usb: 661 | print(texts[language]["no_usb_device"]) 662 | time.sleep(3) 663 | continue 664 | 665 | print(f"[*] USB Device Found: {serial_usb}. Attempting Wi-Fi setup...") 666 | ip = obtener_ip_dispositivo(serial_usb) 667 | if not ip: 668 | print(texts[language]["getting_ip_error"]) 669 | print("[*] Make sure the device is connected to Wi-Fi and USB debugging is enabled.") 670 | time.sleep(4) 671 | continue 672 | 673 | print(f"[*] Device IP: {ip}") 674 | activar_tcpip(serial_usb) 675 | print("[*] TCP/IP mode activated on device.") 676 | time.sleep(2) 677 | conectar_por_wifi(ip, language, texts) 678 | time.sleep(2) 679 | 680 | dispositivos_wifi = obtener_dispositivos(language, texts) 681 | found = False 682 | for d in dispositivos_wifi: 683 | if ip in d.serial: 684 | dispositivo_activo = d 685 | print(f"[+] {texts[language]['connected_to_ip'].format(serial=d.serial)}") 686 | found = True 687 | break 688 | if not found: 689 | print(texts[language]["connect_ip_fail"]) 690 | time.sleep(3) 691 | 692 | elif opcion == "5": # Desconectar Wi-Fi 693 | desconectar_dispositivos_wifi(language, texts) 694 | if dispositivo_activo and ":" in dispositivo_activo.serial: 695 | dispositivo_activo = None 696 | 697 | elif opcion == "6": # Listar dispositivos 698 | dispositivos = obtener_dispositivos(language, texts) 699 | limpiar_pantalla() 700 | if dispositivos: 701 | print(texts[language]["listing_devices"]) 702 | for i, d in enumerate(dispositivos): 703 | active_marker = " (*)" if dispositivo_activo and d.serial == dispositivo_activo.serial else "" 704 | print(texts[language]["device_list_item"].format(index=i + 1, serial=d.serial) + active_marker) 705 | 706 | try: 707 | sel = input(f"{texts[language]['select_device_prompt']} (Enter=Keep current): ").strip() 708 | if sel: 709 | idx = int(sel) - 1 710 | if 0 <= idx < len(dispositivos): 711 | dispositivo_activo = dispositivos[idx] 712 | print(f"[+] Active device set to: {dispositivo_activo.serial}") 713 | else: 714 | print(texts[language]["invalid_option"]) 715 | except ValueError: 716 | if sel: 717 | print(texts[language]["invalid_input_number"]) 718 | except Exception: 719 | print(texts[language]['error_unexpected'].format(error='selection failed')) 720 | 721 | else: 722 | print(texts[language]["no_devices_connected"]) 723 | time.sleep(1) 724 | input(texts[language]["press_enter_continue"]) 725 | 726 | elif opcion == "7" and terminal_main: 727 | target_device = dispositivo_activo 728 | if not target_device: 729 | print("[*] No active device. Please select one command line.") 730 | target_device = seleccionar_dispositivo(language, texts) 731 | if target_device: 732 | terminal_main(target_device) 733 | 734 | elif opcion == "8": # Instalar APK 735 | target_device = dispositivo_activo 736 | if not target_device: 737 | print("[*] No active device. Please select one.") 738 | target_device = seleccionar_dispositivo(language, texts) 739 | 740 | if target_device: 741 | instalar_apk(target_device, language, texts) 742 | else: 743 | print(texts[language]["no_device_selected"]) 744 | time.sleep(3) 745 | 746 | elif opcion == "9": # Abrir scrcpy 747 | target_device = dispositivo_activo 748 | if not target_device: 749 | print("[*] No active device. Please select one to mirror.") 750 | target_device = seleccionar_dispositivo(language, texts) 751 | 752 | if target_device: 753 | try: 754 | abrir_scrcpy(target_device, language, texts) 755 | abrir_scrcpy_linux(target_device, language, texts) 756 | except Exception as e: 757 | print(f"[!] Failed to run scrcpy for Windows: {e}") 758 | print("[*] Attempting to use Linux method...") 759 | try: 760 | abrir_scrcpy_linux(target_device, language, texts) 761 | except Exception as e_linux: 762 | print(f"[X] Failed to run scrcpy for Linux as well: {e_linux}") 763 | time.sleep(3) 764 | else: 765 | print(texts[language]["no_device_selected"]) 766 | time.sleep(3) 767 | 768 | elif opcion == "10": # Obtener info 769 | target_device = dispositivo_activo 770 | if not target_device: 771 | print("[*] No active device. Please select one.") 772 | target_device = seleccionar_dispositivo(language, texts) 773 | 774 | if target_device: 775 | obtener_info_dispositivo(target_device, language, texts) 776 | else: 777 | print(texts[language]["no_device_selected"]) 778 | time.sleep(3) 779 | 780 | elif opcion == "11": # Listar apps 781 | target_device = dispositivo_activo 782 | if not target_device: 783 | print("[*] No active device. Please select one.") 784 | target_device = seleccionar_dispositivo(language, texts) 785 | 786 | if target_device: 787 | listar_aplicaciones(target_device, language, texts) 788 | else: 789 | print(texts[language]["no_device_selected"]) 790 | time.sleep(3) 791 | 792 | elif opcion == "12": # Conectar por IP 793 | connected_device = conectar_por_ip(language, texts) 794 | if connected_device: 795 | dispositivo_activo = connected_device 796 | 797 | elif opcion == "13" and file_explorer_main: # Abrir admin de archivos 798 | target_device = dispositivo_activo 799 | if not target_device: 800 | print("[*] No active device. Please select one for file explorer.") 801 | target_device = seleccionar_dispositivo(language, texts) 802 | 803 | if target_device: 804 | print(texts[language]["opening_file_explorer"]) 805 | time.sleep(1) # Brief pause before curses takes over 806 | limpiar_pantalla() 807 | try: 808 | curses.wrapper(lambda stdscr: file_explorer_main(stdscr, target_device)) 809 | print(f"\n{texts[language]['file_explorer_closed']}") 810 | except NameError: 811 | print("[!] File explorer module not loaded correctly.") 812 | except Exception as e: 813 | print(f"\n{texts[language]['file_explorer_error'].format(error=e)}") 814 | time.sleep(3) 815 | 816 | else: 817 | print(texts[language]["no_device_selected"]) 818 | time.sleep(3) 819 | 820 | elif opcion == "14" and adb_sideload_main: # ADB sideload 821 | target_device = dispositivo_activo 822 | if not target_device: 823 | print(texts[language]["no_active_device_sideload"]) 824 | target_device = seleccionar_dispositivo(language, texts) 825 | 826 | if target_device: 827 | print(texts[language]["starting_sideload"]) 828 | time.sleep(1) # Breve pausa para coherencia con el explorador de archivos 829 | limpiar_pantalla() 830 | try: 831 | adb_sideload_main(target_device) 832 | print(f"\n{texts[language]['sideload_closed']}") 833 | except NameError: 834 | print(texts[language]["sideload_module_error"]) 835 | except Exception as e: 836 | import traceback 837 | print(f"\n{texts[language]['sideload_error'].format(error=e)}") 838 | traceback.print_exc() # Imprime el traceback para depuración 839 | time.sleep(3) 840 | else: 841 | print(texts[language]["no_device_selected"]) 842 | time.sleep(3) 843 | 844 | 845 | 846 | elif opcion == "15": # Salir 847 | print(texts[language]["exiting"]) 848 | time.sleep(1) 849 | limpiar_pantalla() 850 | break 851 | 852 | else: 853 | print(texts[language]["invalid_option"]) 854 | time.sleep(2) 855 | 856 | # --- Entry Point --- 857 | if __name__ == "__main__": 858 | language = 'en' 859 | try: 860 | language = seleccionar_idioma(TEXTS) 861 | cli_adb(language, TEXTS) 862 | except KeyboardInterrupt: 863 | limpiar_pantalla() 864 | print(TEXTS[language]["interrupted_keyboard"]) 865 | except Exception as e: 866 | limpiar_pantalla() 867 | print(TEXTS[language]["error_unexpected"].format(error=e)) 868 | input("\nPress Enter to exit.") -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ADBuster 2 | 3 | For Readme in English: https://github.com/re-3v0lv3d/ADBuster/blob/main/Readme-EN.MD 4 | 5 | 6 | 7 | **ADBuster** es una herramienta interactiva escrita en Python que facilita el control y gestión avanzada de dispositivos Android mediante ADB (Android Debug Bridge). Diseñada con una interfaz CLI estilo menú, esta utilidad está pensada tanto para usuarios técnicos como para entusiastas de Android que desean automatizar tareas comunes y realizar operaciones complejas sobre dispositivos conectados por USB o Wi-Fi. 8 | 9 | 10 | ![Captura de pantalla 2025-04-19 194648](https://github.com/user-attachments/assets/705e8666-0ee7-45e8-aa85-8b1c19e884f6) 11 | 12 | 13 | 14 | ## ✨ Características principales 15 | 16 | - 🖥 **Interfaz de Menú Interactiva**: Navegación intuitiva desde la terminal, con soporte bilingüe (Español/Inglés). 17 | - 🔌 **Conexión Flexible**: Compatible con dispositivos conectados por USB o mediante IP a través de Wi-Fi. 18 | - 🛠 **Gestión Completa del ADB**: Iniciar/detener servidor ADB, listar dispositivos, ejecutar comandos personalizados. 19 | - 🚀 **Reinicios Personalizados**: Reinicio del dispositivo en modo normal, recovery, fastboot o EDL. 20 | - 📲 **Instalación de APKs**: Selección gráfica del archivo APK desde el explorador del sistema y su instalación directa en el dispositivo. 21 | - 📡 **Bypass de USB a Wi-Fi**: Transición automática del modo USB al modo ADB over Wi-Fi. 22 | - 🔍 **Información del Dispositivo**: Consulta de fabricante, modelo y versión de Android. 23 | - 🗂 **Explorador de Archivos Integrado**: Interfaz basada en `curses` para explorar archivos remotos del dispositivo (si el módulo está presente). 24 | - 🎮 **scrcpy Integrado**: Refleja y controla la pantalla del dispositivo Android desde el escritorio (requiere scrcpy.exe). 25 | - 📦 **Listado de Aplicaciones Instaladas**: Visualización de todos los paquetes de usuario instalados. 26 | - 🧠 **Extensibilidad Modular**: Soporte para scripts externos como `adb_file_explorer.py` o `terminal.py`. 27 | - Soporte Inglés-Español. 28 | 29 | "Añadido soporte Adb sideload ;)" 30 | 31 | 32 | 33 | ![Captura de pantalla 2025-04-19 195154](https://github.com/user-attachments/assets/428b0d3e-df36-4c35-9d51-f46651154605) 34 | 35 | 36 | 37 | ![Captura de pantalla 2025-04-19 154056](https://github.com/user-attachments/assets/e1d01d14-758b-4f97-9e78-45dec42e0cd2) 38 | 39 | 40 | 41 | ![Captura de pantalla 2025-04-19 154146](https://github.com/user-attachments/assets/ec6ecdbd-f2f3-450b-9128-f2a4c8993b44) 42 | 43 | 44 | 45 | 46 | ## 📦 Requisitos 47 | 48 | - ADB Instalado 49 | - Python 3.8+ 50 | - `pure-python-adb` 51 | - `scrcpy` (colocado en `./scrcpy/` para Windows o Linux) MUY IMPORTANTE 52 | - Sistema operativo con soporte para consola (`cmd`, `bash`, etc.) 53 | - Dispositivo Android con depuración USB activada 54 | 55 | Instalación de dependencias: 56 | ```bash 57 | pip install pure-python-adb 58 | ``` 59 | 60 | (Solo windows) 61 | 62 | ```bash 63 | pip install windows-curses 64 | ``` 65 | 66 | ## 🚀 Uso 67 | 68 | Ejecuta el script principal desde tu terminal: 69 | 70 | ```bash 71 | python ADBuster.py 72 | ``` 73 | 74 | 75 | 76 | 77 | ![Captura de pantalla 2025-04-19 154438](https://github.com/user-attachments/assets/cc8ef3d5-3b0c-4394-bf94-66d9648cd360) 78 | 79 | 80 | Selecciona el idioma, y navega por las distintas opciones del menú para interactuar con los dispositivos conectados. 81 | 82 | ## 📁 Estructura recomendada 83 | 84 | ``` 85 | ADBuster/ 86 | │ 87 | ├── ADBuster.py 88 | ├── scripts/ 89 | │ ├── adb_file_explorer.py (opcional) 90 | │ └── terminal.py (opcional) 91 | ├── scrcpy/ 92 | │ └── scrcpy.exe (Windows) o `scrcpy` (Linux) 93 | ``` 94 | 95 | ## 🧠 Notas adicionales 96 | 97 | - Algunas funciones como el explorador de archivos o terminal requieren módulos auxiliares. 98 | - Para conexión Wi-Fi, asegúrate de que el dispositivo esté en la misma red que el host y habilitado en modo TCP/IP ADB. 99 | 100 | ## 📜 Licencia 101 | 102 | Este proyecto es de código abierto bajo la licencia MIT. Siéntete libre de modificarlo, adaptarlo o distribuirlo bajo tus propios términos. 103 | Gracias a Genymobile por el lanzamiento y mantenimiento de Scrcpy 104 | 105 | 106 | ``` 107 | GNU GENERAL PUBLIC LICENSE 108 | Version 2, June 1991 109 | 110 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 111 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 112 | Everyone is permitted to copy and distribute verbatim copies 113 | of this license document, but changing it is not allowed. 114 | 115 | Preamble 116 | 117 | The licenses for most software are designed to take away your 118 | freedom to share and change it. By contrast, the GNU General Public 119 | License is intended to guarantee your freedom to share and change free 120 | software--to make sure the software is free for all its users. This 121 | General Public License applies to most of the Free Software 122 | Foundation's software and to any other program whose authors commit to 123 | using it. (Some other Free Software Foundation software is covered by 124 | the GNU Library General Public License instead.) You can apply it to 125 | your programs, too. 126 | 127 | When we speak of free software, we are referring to freedom, not 128 | price. Our General Public Licenses are designed to make sure that you 129 | have the freedom to distribute copies of free software (and charge for 130 | this service if you wish), that you receive source code or can get it 131 | if you want it, that you can change the software or use pieces of it 132 | in new free programs; and that you know you can do these things. 133 | 134 | To protect your rights, we need to make restrictions that forbid 135 | anyone to deny you these rights or to ask you to surrender the rights. 136 | These restrictions translate to certain responsibilities for you if you 137 | distribute copies of the software, or if you modify it. 138 | 139 | For example, if you distribute copies of such a program, whether 140 | gratis or for a fee, you must give the recipients all the rights that 141 | you have. You must make sure that they, too, receive or can get the 142 | source code. And you must show them these terms so they know their 143 | rights. 144 | 145 | We protect your rights with two steps: (1) copyright the software, and 146 | (2) offer you this license which gives you legal permission to copy, 147 | distribute and/or modify the software. 148 | 149 | Also, for each author's protection and ours, we want to make certain 150 | that everyone understands that there is no warranty for this free 151 | software. If the software is modified by someone else and passed on, we 152 | want its recipients to know that what they have is not the original, so 153 | that any problems introduced by others will not reflect on the original 154 | authors' reputations. 155 | 156 | Finally, any free program is threatened constantly by software 157 | patents. We wish to avoid the danger that redistributors of a free 158 | program will individually obtain patent licenses, in effect making the 159 | program proprietary. To prevent this, we have made it clear that any 160 | patent must be licensed for everyone's free use or not licensed at all. 161 | 162 | The precise terms and conditions for copying, distribution and 163 | modification follow.``` 164 | -------------------------------------------------------------------------------- /Readme-EN.MD: -------------------------------------------------------------------------------- 1 | # ADBuster 2 | 3 | **ADBuster** is an interactive tool written in Python that facilitates advanced control and management of Android devices via ADB (Android Debug Bridge). Designed with a menu-style CLI interface, this utility is intended for both technical users and Android enthusiasts looking to automate common tasks and perform complex operations on devices connected via USB or Wi-Fi. 4 | 5 | "Added ADB Sideload support" 6 | 7 | ## ✨ Main Features 8 | 9 | - 🖥 **Interactive Menu Interface**: Intuitive terminal navigation, with bilingual support (Spanish/English). 10 | - 🔌 **Flexible Connection**: Compatible with devices connected via USB or IP over Wi-Fi. 11 | - 🛠 **Full ADB Management**: Start/stop ADB server, list devices, run custom commands. 12 | - 🚀 **Custom Reboots**: Reboot the device in normal, recovery, fastboot, or EDL mode. 13 | - 📲 **APK Installation**: Graphical file selection for APKs and direct installation to the device. 14 | - 📡 **USB to Wi-Fi Bypass**: Automatic switch from USB mode to ADB over Wi-Fi. 15 | - 🔍 **Device Information**: Query manufacturer, model, and Android version. 16 | - 🗂 **Integrated File Explorer**: `curses`-based interface for exploring remote device files (if module is present). 17 | - 🎮 **scrcpy Integration**: Mirror and control the Android device screen from desktop (requires scrcpy.exe). 18 | - 📦 **Installed Apps Listing**: View all user-installed packages. 19 | - 🧠 **Modular Extensibility**: Supports external scripts like `adb_file_explorer.py` or `terminal.py`. 20 | - 🌐 **English-Spanish Support**. 21 | 22 | ![Screenshot 2025-04-19 154056](https://github.com/user-attachments/assets/e1d01d14-758b-4f97-9e78-45dec42e0cd2) 23 | 24 | ![Screenshot 2025-04-19 154146](https://github.com/user-attachments/assets/ec6ecdbd-f2f3-450b-9128-f2a4c8993b44) 25 | 26 | ## 📦 Requirements 27 | 28 | - ADB Installed 29 | - Python 3.8+ 30 | - `pure-python-adb` 31 | - `scrcpy` (placed in `./scrcpy/` for Windows or Linux) **VERY IMPORTANT** 32 | - OS with console support (`cmd`, `bash`, etc.) 33 | - Android device with USB debugging enabled 34 | 35 | Install dependencies: 36 | ```bash 37 | pip install pure-python-adb 38 | ``` 39 | 40 | On Windows: 41 | ```bash 42 | pip install windows-curses 43 | ``` 44 | 45 | ## 🚀 Usage 46 | 47 | Run the main script from your terminal: 48 | 49 | ```bash 50 | python ADBuster.py 51 | ``` 52 | 53 | ![Screenshot 2025-04-19 154438](https://github.com/user-attachments/assets/cc8ef3d5-3b0c-4394-bf94-66d9648cd360) 54 | 55 | Select your language and navigate through the various menu options to interact with connected devices. 56 | 57 | ## 📁 Recommended Structure 58 | 59 | ``` 60 | ADBuster/ 61 | │ 62 | ├── ADBuster.py 63 | ├── scripts/ 64 | │ ├── adb_file_explorer.py (optional) 65 | │ └── terminal.py (optional) 66 | ├── scrcpy/ 67 | │ └── scrcpy.exe (Windows) or `scrcpy` (Linux) 68 | ``` 69 | 70 | ## 🧠 Additional Notes 71 | 72 | - Some features like the file explorer or terminal require auxiliary modules. 73 | - For Wi-Fi connection, ensure the device is on the same network as the host and ADB TCP/IP mode is enabled. 74 | 75 | ## 📜 License 76 | 77 | This project is open-source under the GPLv2 license. Feel free to modify, adapt, or distribute it under the same terms. 78 | Thanks to Genymobile for releasing and maintaining scrcpy. 79 | 80 | ``` 81 | GNU GENERAL PUBLIC LICENSE 82 | Version 2, June 1991 83 | 84 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 85 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 86 | Everyone is permitted to copy and distribute verbatim copies 87 | of this license document, but changing it is not allowed. 88 | 89 | Preamble 90 | 91 | The licenses for most software are designed to take away your 92 | freedom to share and change it. By contrast, the GNU General Public 93 | License is intended to guarantee your freedom to share and change free 94 | software--to make sure the software is free for all its users. This 95 | General Public License applies to most of the Free Software 96 | Foundation's software and to any other program whose authors commit to 97 | using it. (Some other Free Software Foundation software is covered by 98 | the GNU Library General Public License instead.) You can apply it to 99 | your programs, too. 100 | 101 | When we speak of free software, we are referring to freedom, not 102 | price. Our General Public Licenses are designed to make sure that you 103 | have the freedom to distribute copies of free software (and charge for 104 | this service if you wish), that you receive source code or can get it 105 | if you want it, that you can change the software or use pieces of it 106 | in new free programs; and that you know you can do these things. 107 | 108 | To protect your rights, we need to make restrictions that forbid 109 | anyone to deny you these rights or to ask you to surrender the rights. 110 | These restrictions translate to certain responsibilities for you if you 111 | distribute copies of the software, or if you modify it. 112 | 113 | For example, if you distribute copies of such a program, whether 114 | gratis or for a fee, you must give the recipients all the rights that 115 | you have. You must make sure that they, too, receive or can get the 116 | source code. And you must show them these terms so they know their 117 | rights. 118 | 119 | We protect your rights with two steps: (1) copyright the software, and 120 | (2) offer you this license which gives you legal permission to copy, 121 | distribute and/or modify the software. 122 | 123 | Also, for each author's protection and ours, we want to make certain 124 | that everyone understands that there is no warranty for this free 125 | software. If the software is modified by someone else and passed on, we 126 | want its recipients to know that what they have is not the original, so 127 | that any problems introduced by others will not reflect on the original 128 | authors' reputations. 129 | 130 | Finally, any free program is threatened constantly by software 131 | patents. We wish to avoid the danger that redistributors of a free 132 | program will individually obtain patent licenses, in effect making the 133 | program proprietary. To prevent this, we have made it clear that any 134 | patent must be licensed for everyone's free use or not licensed at all. 135 | 136 | The precise terms and conditions for copying, distribution and 137 | modification follow. 138 | ``` 139 | -------------------------------------------------------------------------------- /__pycache__/adb_file_explorer.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/__pycache__/adb_file_explorer.cpython-313.pyc -------------------------------------------------------------------------------- /__pycache__/terminal.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/__pycache__/terminal.cpython-313.pyc -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pure-python-adb==0.3.0.dev0 2 | -------------------------------------------------------------------------------- /scrcpy/AdbWinApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/AdbWinApi.dll -------------------------------------------------------------------------------- /scrcpy/AdbWinUsbApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/AdbWinUsbApi.dll -------------------------------------------------------------------------------- /scrcpy/SDL2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/SDL2.dll -------------------------------------------------------------------------------- /scrcpy/adb.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/adb.exe -------------------------------------------------------------------------------- /scrcpy/avcodec-61.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/avcodec-61.dll -------------------------------------------------------------------------------- /scrcpy/avformat-61.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/avformat-61.dll -------------------------------------------------------------------------------- /scrcpy/avutil-59.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/avutil-59.dll -------------------------------------------------------------------------------- /scrcpy/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/icon.png -------------------------------------------------------------------------------- /scrcpy/libusb-1.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/libusb-1.0.dll -------------------------------------------------------------------------------- /scrcpy/open_a_terminal_here.bat: -------------------------------------------------------------------------------- 1 | @cmd 2 | -------------------------------------------------------------------------------- /scrcpy/scrcpy-console.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | scrcpy.exe --pause-on-exit=if-error %* 3 | -------------------------------------------------------------------------------- /scrcpy/scrcpy-noconsole.vbs: -------------------------------------------------------------------------------- 1 | strCommand = "cmd /c scrcpy.exe" 2 | 3 | For Each Arg In WScript.Arguments 4 | strCommand = strCommand & " """ & replace(Arg, """", """""""""") & """" 5 | Next 6 | 7 | CreateObject("Wscript.Shell").Run strCommand, 0, false 8 | -------------------------------------------------------------------------------- /scrcpy/scrcpy-server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/scrcpy-server -------------------------------------------------------------------------------- /scrcpy/scrcpy.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/scrcpy.exe -------------------------------------------------------------------------------- /scrcpy/swresample-5.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scrcpy/swresample-5.dll -------------------------------------------------------------------------------- /scripts/__pycache__/adb_fastboot_partitions.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scripts/__pycache__/adb_fastboot_partitions.cpython-313.pyc -------------------------------------------------------------------------------- /scripts/__pycache__/adb_file_explorer.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scripts/__pycache__/adb_file_explorer.cpython-313.pyc -------------------------------------------------------------------------------- /scripts/__pycache__/adb_sideload.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scripts/__pycache__/adb_sideload.cpython-313.pyc -------------------------------------------------------------------------------- /scripts/__pycache__/terminal.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/re-3v0lv3d/ADBuster/c505320551f6b542e80da78527855bccc068fbfd/scripts/__pycache__/terminal.cpython-313.pyc -------------------------------------------------------------------------------- /scripts/adb_file_explorer.py: -------------------------------------------------------------------------------- 1 | import curses 2 | import os 3 | import re 4 | import subprocess 5 | from typing import List, Dict, Optional 6 | from ppadb.client import Client as AdbClient 7 | from tkinter import Tk, filedialog 8 | 9 | # Ventanita de tkinter para seleccionar archivos. La escondo porque solo quiero el diálogo. 10 | root = Tk() 11 | root.withdraw() 12 | 13 | # Diccionario con los textos en español e inglés. Me costó un poco organizarlo, pero así queda claro qué mensaje va dónde. 14 | # Lo hice con formato para poder meter nombres de archivos o errores dinámicamente. 15 | TEXTS = { 16 | "es": { 17 | "select_language": "Seleccione idioma / Select language:", 18 | "language_option_1": "1. Español", 19 | "language_option_2": "2. English", 20 | "select_language_prompt": "Seleccione 1 o 2 y Enter: ", 21 | "invalid_language": "¡Opción no válida! Presiona Enter para intentarlo otra vez.", 22 | "connected_devices": "Dispositivos conectados:", 23 | "select_device_prompt": "Elige un número y pulsa Enter: ", 24 | "enter_number": "Tienes que poner un número, ¡venga!", 25 | "no_device_selected": "No seleccionaste ningún dispositivo. Cerrando...", 26 | "connected_to": "¡Conectado al dispositivo: {serial}!", 27 | "browsing": "Explorando: {path}", 28 | "actions": "Acciones: [q] Salir, [u] Subir archivo, [n] Descargar, [r] Ir a ruta, [d] Eliminar, [Enter] Entrar/Volver", 29 | "go_to_path": "Escribe la ruta: {buffer}", 30 | "delete_confirm": "¿Seguro que quieres eliminar '{name}'? (s/n)", 31 | "uploading": "Subiendo '{name}'... ¡Espera un momento!", 32 | "upload_success": "¡'{name}' subido con éxito!", 33 | "upload_error": "Error al subir '{name}'. Algo salió mal :(", 34 | "cannot_download_dotdot": "No puedes descargar '..'... ¿qué intentas hacer? :P", 35 | "download_dir_not_allowed": "¡No se pueden descargar carpetas! Solo archivos.", 36 | "downloading": "Descargando '{name}'... ¡Ya casi!", 37 | "download_success": "¡'{name}' descargado correctamente!", 38 | "download_error": "Error al descargar: {error}", 39 | "download_cancelled": "Descarga cancelada. No seleccionaste dónde guardarlo.", 40 | "cannot_delete_dotdot": "No puedes eliminar '..' ¡Eso es trampa!", 41 | "delete_success": "¡'{name}' eliminado sin problemas!", 42 | "delete_error": "Error al eliminar: {error}. Puede que no tengas permisos.", 43 | "delete_cancelled": "Eliminación cancelada. Todo sigue en su sitio.", 44 | }, 45 | "en": { 46 | "select_language": "Select language / Seleccione idioma:", 47 | "language_option_1": "1. Spanish", 48 | "language_option_2": "2. English", 49 | "select_language_prompt": "Pick 1 or 2 and hit Enter: ", 50 | "invalid_language": "Invalid choice! Hit Enter to try again.", 51 | "connected_devices": "Connected devices:", 52 | "select_device_prompt": "Choose a number and press Enter: ", 53 | "enter_number": "You gotta enter a number, come on!", 54 | "no_device_selected": "No device selected. Shutting down...", 55 | "connected_to": "Connected to device: {serial}!", 56 | "browsing": "Browsing: {path}", 57 | "actions": "Actions: [q] Quit, [u] Upload file, [n] Download, [r] Go to path, [d] Delete, [Enter] Enter/Back", 58 | "go_to_path": "Enter path: {buffer}", 59 | "delete_confirm": "Are you sure you want to delete '{name}'? (y/n)", 60 | "uploading": "Uploading '{name}'... Hang on a sec!", 61 | "upload_success": "'{name}' uploaded successfully!", 62 | "upload_error": "Error uploading '{name}'. Something went wrong :(", 63 | "cannot_download_dotdot": "You can't download '..'... What's that about? :P", 64 | "download_dir_not_allowed": "Can't download folders! Files only, please.", 65 | "downloading": "Downloading '{name}'... Almost there!", 66 | "download_success": "'{name}' downloaded successfully!", 67 | "download_error": "Error downloading: {error}", 68 | "download_cancelled": "Download cancelled. You didn't pick a save location.", 69 | "cannot_delete_dotdot": "You can't delete '..'! That's cheating!", 70 | "delete_success": "'{name}' deleted without a hitch!", 71 | "delete_error": "Error deleting: {error}. Maybe you don't have permission.", 72 | "delete_cancelled": "Deletion cancelled. Everything's still there.", 73 | } 74 | } 75 | 76 | def pick_language(screen): 77 | """Primera pantalla para elegir idioma. Quise hacerla sencilla, solo 1 o 2.""" 78 | curses.curs_set(0) # Oculta el cursor para que quede más limpio 79 | screen.clear() 80 | screen.addstr(0, 0, TEXTS["es"]["select_language"]) 81 | screen.addstr(1, 0, TEXTS["es"]["language_option_1"]) 82 | screen.addstr(2, 0, TEXTS["es"]["language_option_2"]) 83 | screen.addstr(4, 0, TEXTS["es"]["select_language_prompt"]) 84 | screen.refresh() 85 | 86 | choice = "" 87 | while True: 88 | key = screen.getch() 89 | if key in (ord('1'), ord('2')): 90 | choice = chr(key) 91 | screen.addstr(4, len(TEXTS["es"]["select_language_prompt"]), choice) 92 | screen.refresh() 93 | elif key in (curses.KEY_ENTER, 10): 94 | if choice == "1": 95 | return "es" 96 | elif choice == "2": 97 | return "en" 98 | else: 99 | screen.addstr(6, 0, TEXTS["es"]["invalid_language"]) 100 | screen.refresh() 101 | elif key == 27: # Si presionan Esc, español por defecto 102 | return "es" 103 | elif key in (curses.KEY_BACKSPACE, 127, 8): 104 | choice = "" 105 | screen.move(4, len(TEXTS["es"]["select_language_prompt"])) 106 | screen.delch() 107 | screen.refresh() 108 | 109 | def run_adb_command(device, command): 110 | """Ejecuta comandos ADB en el dispositivo. A veces falla si la conexión no es estable, pero lo manejo con try/except.""" 111 | try: 112 | return device.shell(command).strip().splitlines() 113 | except Exception as e: 114 | return [f"Error: {e}"] 115 | 116 | def list_files_and_folders(device, current_path): 117 | """Lista archivos y carpetas en el directorio actual. Tuve que ajustar el regex porque a veces el ls -l devuelve formatos raros.""" 118 | items = [] 119 | for line in run_adb_command(device, f"ls -l '{current_path}'"): 120 | parts = re.split(r'\s+', line.strip(), maxsplit=8) 121 | if len(parts) >= 9 and parts[-1] not in (".", ".."): 122 | items.append({"type": "DIR" if parts[0].startswith("d") else "FILE", "name": parts[-1]}) 123 | elif len(parts) >= 8 and (line.startswith("-") or line.startswith("d")) and parts[-1] not in (".", ".."): 124 | items.append({"type": "DIR" if parts[0].startswith("d") else "FILE", "name": parts[-1]}) 125 | return [{"type": "DIR", "name": ".."}] + items 126 | 127 | def upload_file(device, local_path, remote_path, screen, max_y, language): 128 | """Sube un archivo al dispositivo con adb push. Puse un timeout porque una vez se quedó colgado con un archivo grande.""" 129 | cmd = ["adb", "-s", device.serial, "push", local_path, remote_path] 130 | try: 131 | subprocess.run(cmd, capture_output=True, text=True, check=True, timeout=300) 132 | screen.addstr(max_y - 3, 0, TEXTS[language]["upload_success"].format(name=os.path.basename(local_path))) 133 | screen.clrtoeol() 134 | screen.refresh() 135 | screen.getch() 136 | return True 137 | except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired) as e: 138 | screen.addstr(max_y - 3, 0, TEXTS[language]["upload_error"].format(name=os.path.basename(local_path))) 139 | screen.clrtoeol() 140 | screen.refresh() 141 | screen.getch() 142 | return False 143 | 144 | def download_file(device, remote_path, local_path, screen, max_y, language): 145 | """Descarga un archivo con adb pull. Igual que upload_file, con timeout para evitar problemas.""" 146 | if not local_path: 147 | screen.addstr(max_y - 3, 0, TEXTS[language]["download_cancelled"]) 148 | screen.clrtoeol() 149 | screen.refresh() 150 | screen.getch() 151 | return False 152 | 153 | cmd = ["adb", "-s", device.serial, "pull", remote_path, local_path] 154 | try: 155 | subprocess.run(cmd, capture_output=True, text=True, check=True, timeout=300) 156 | screen.addstr(max_y - 3, 0, TEXTS[language]["download_success"].format(name=os.path.basename(remote_path))) 157 | screen.clrtoeol() 158 | screen.refresh() 159 | screen.getch() 160 | return True 161 | except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired) as e: 162 | screen.addstr(max_y - 3, 0, TEXTS[language]["download_error"].format(error=str(e))) 163 | screen.clrtoeol() 164 | screen.refresh() 165 | screen.getch() 166 | return False 167 | 168 | def delete_item(device, current_path, item, screen, max_y, language): 169 | """Elimina un archivo o carpeta. Usé rm -r para carpetas porque rm solo no funciona. Ojo con los permisos!""" 170 | if item["name"] == "..": 171 | screen.addstr(max_y - 3, 0, TEXTS[language]["cannot_delete_dotdot"]) 172 | screen.clrtoeol() 173 | screen.refresh() 174 | screen.getch() 175 | return False 176 | 177 | # Escapo el nombre por si tiene caracteres raros 178 | item_name = item["name"].replace("'", "'\\''") 179 | full_path = os.path.join(current_path, item_name).replace("'", "'\\''") 180 | cmd = f"rm -r '{full_path}'" if item["type"] == "DIR" else f"rm '{full_path}'" 181 | 182 | try: 183 | result = run_adb_command(device, cmd) 184 | if any("Error" in line for line in result): 185 | screen.addstr(max_y - 3, 0, TEXTS[language]["delete_error"].format(error=result[0])) 186 | screen.clrtoeol() 187 | screen.refresh() 188 | screen.getch() 189 | return False 190 | screen.addstr(max_y - 3, 0, TEXTS[language]["delete_success"].format(name=item["name"])) 191 | screen.clrtoeol() 192 | screen.refresh() 193 | screen.getch() 194 | return True 195 | except Exception as e: 196 | screen.addstr(max_y - 3, 0, TEXTS[language]["delete_error"].format(error=str(e))) 197 | screen.clrtoeol() 198 | screen.refresh() 199 | screen.getch() 200 | return False 201 | 202 | def file_explorer(screen, device, language): 203 | """La función principal. Aquí pasa toda la acción: navegar, subir, descargar, eliminar... Fue un lío hacer la interfaz!""" 204 | current_path = "/sdcard/" # Empiezo en sdcard porque es lo más común 205 | items = [] 206 | cursor_pos = 0 207 | top_item = 0 208 | max_y, max_x = screen.getmaxyx() 209 | entering_path = False 210 | path_buffer = "" 211 | deleting = False 212 | 213 | def refresh_items(): 214 | nonlocal items 215 | items = list_files_and_folders(device, current_path) 216 | 217 | def draw_screen(): 218 | screen.clear() 219 | screen.addstr(0, 0, TEXTS[language]["browsing"].format(path=current_path), curses.A_BOLD) 220 | for i in range(top_item, min(top_item + max_y - 4, len(items))): 221 | item = items[i] 222 | attr = curses.A_REVERSE if i == cursor_pos else curses.A_NORMAL 223 | screen.addstr(i - top_item + 2, 0, f"[{item['type']}] {item['name']}", attr) 224 | screen.addstr(max_y - 2, 0, TEXTS[language]["actions"], curses.A_DIM) 225 | if entering_path: 226 | screen.addstr(max_y - 1, 0, TEXTS[language]["go_to_path"].format(buffer=path_buffer), curses.A_NORMAL) 227 | screen.clrtoeol() 228 | elif deleting: 229 | screen.addstr(max_y - 1, 0, TEXTS[language]["delete_confirm"].format(name=items[cursor_pos]["name"]), curses.A_NORMAL) 230 | screen.clrtoeol() 231 | screen.refresh() 232 | 233 | refresh_items() 234 | draw_screen() 235 | 236 | while True: 237 | key = screen.getch() 238 | 239 | # Modo de escribir ruta 240 | if entering_path: 241 | if key in (curses.KEY_ENTER, 10): 242 | current_path = path_buffer.strip() 243 | refresh_items() 244 | cursor_pos = top_item = 0 245 | entering_path = False 246 | path_buffer = "" 247 | elif key == 27: 248 | entering_path = False 249 | path_buffer = "" 250 | elif key in (curses.KEY_BACKSPACE, 127, 8): 251 | path_buffer = path_buffer[:-1] 252 | elif 32 <= key <= 126: 253 | path_buffer += chr(key) 254 | draw_screen() 255 | continue 256 | 257 | # Modo de confirmar eliminación 258 | if deleting: 259 | confirm_key = 'y' if language == "en" else 's' 260 | if key == ord(confirm_key): 261 | deleting = False 262 | if delete_item(device, current_path, items[cursor_pos], screen, max_y, language): 263 | refresh_items() 264 | cursor_pos = min(cursor_pos, len(items) - 1) 265 | top_item = min(top_item, max(0, len(items) - (max_y - 4))) 266 | elif key in (ord('n'), 27): 267 | deleting = False 268 | screen.addstr(max_y - 3, 0, TEXTS[language]["delete_cancelled"]) 269 | screen.clrtoeol() 270 | screen.refresh() 271 | screen.getch() 272 | draw_screen() 273 | continue 274 | 275 | # Navegación con flechas 276 | if key == curses.KEY_DOWN: 277 | cursor_pos = min(cursor_pos + 1, len(items) - 1) 278 | if cursor_pos >= top_item + max_y - 4: 279 | top_item += 1 280 | elif key == curses.KEY_UP: 281 | cursor_pos = max(cursor_pos - 1, 0) 282 | if cursor_pos < top_item: 283 | top_item -= 1 284 | # Entrar en carpeta o volver atrás 285 | elif key in (curses.KEY_ENTER, 10): 286 | selected = items[cursor_pos] 287 | if selected["name"] == "..": 288 | current_path = os.path.dirname(current_path.rstrip('/')) + '/' 289 | refresh_items() 290 | cursor_pos = top_item = 0 291 | elif selected["type"] == "DIR": 292 | current_path = os.path.join(current_path, selected["name"]) + '/' 293 | refresh_items() 294 | cursor_pos = top_item = 0 295 | # Subir archivo 296 | elif key == ord('u'): 297 | file_to_upload = filedialog.askopenfilename() 298 | if file_to_upload: 299 | screen.addstr(max_y - 3, 0, TEXTS[language]["uploading"].format(name=os.path.basename(file_to_upload))) 300 | screen.refresh() 301 | destination = os.path.join(current_path, os.path.basename(file_to_upload)) 302 | upload_file(device, file_to_upload, destination, screen, max_y, language) 303 | refresh_items() 304 | top_item = 0 305 | cursor_pos = 0 306 | # Descargar archivo 307 | elif key == ord('n'): 308 | selected = items[cursor_pos] 309 | if selected["name"] == "..": 310 | screen.addstr(max_y - 3, 0, TEXTS[language]["cannot_download_dotdot"]) 311 | screen.clrtoeol() 312 | screen.refresh() 313 | screen.getch() 314 | elif selected["type"] == "DIR": 315 | screen.addstr(max_y - 3, 0, TEXTS[language]["download_dir_not_allowed"]) 316 | screen.clrtoeol() 317 | screen.refresh() 318 | screen.getch() 319 | else: 320 | source_path = os.path.join(current_path, selected["name"]) 321 | save_path = filedialog.asksaveasfilename(initialfile=selected["name"], 322 | title="Save file as" if language == "en" else "Guardar archivo como") 323 | if save_path: 324 | screen.addstr(max_y - 3, 0, TEXTS[language]["downloading"].format(name=selected["name"])) 325 | screen.refresh() 326 | download_file(device, source_path, save_path, screen, max_y, language) 327 | # Entrar en modo escribir ruta 328 | elif key == ord('r'): 329 | entering_path = True 330 | path_buffer = "" 331 | # Eliminar archivo o carpeta 332 | elif key == ord('d'): 333 | deleting = True 334 | # Salir 335 | elif key == ord('q'): 336 | break 337 | 338 | draw_screen() 339 | 340 | def main(screen, device=None): 341 | """Punto de entrada. Aquí empieza todo. Si me pasan un dispositivo, lo uso; si no, cierro con un mensaje.""" 342 | curses.curs_set(0) 343 | 344 | # Elegir idioma 345 | language = pick_language(screen) 346 | 347 | # Usar el dispositivo pasado como argumento, si existe 348 | if device: 349 | screen.clear() 350 | screen.addstr(0, 0, TEXTS[language]["connected_to"].format(serial=device.serial), curses.A_BOLD) 351 | screen.refresh() 352 | curses.napms(1000) # Pausa para que se vea el mensaje 353 | file_explorer(screen, device, language) 354 | else: 355 | screen.clear() 356 | screen.addstr(0, 0, TEXTS[language]["no_device_selected"], curses.A_BOLD) 357 | screen.refresh() 358 | curses.napms(1500) 359 | 360 | if __name__ == "__main__": 361 | try: 362 | curses.wrapper(main) 363 | except Exception as e: 364 | print(f"Oops, algo falló: {e}") 365 | curses.endwin() -------------------------------------------------------------------------------- /scripts/adb_sideload.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import subprocess 4 | import re 5 | import tkinter as tk 6 | from tkinter import filedialog 7 | from ppadb.device import Device 8 | 9 | def limpiar_pantalla(): 10 | """Limpia la pantalla de la terminal.""" 11 | os.system("cls" if os.name == "nt" else "clear") 12 | 13 | def select_file_with_tkinter() -> str | None: 14 | """Abre un diálogo de Tkinter para seleccionar un archivo APK o ZIP.""" 15 | root = tk.Tk() 16 | root.withdraw() # Oculta la ventana principal 17 | file_path = filedialog.askopenfilename( 18 | filetypes=[("Archivos APK o ZIP", "*.apk *.zip"), ("Todos los archivos", "*.*")] 19 | ) 20 | root.destroy() # Limpia la instancia de Tkinter 21 | return file_path if file_path else None 22 | 23 | def sideload_file(device: Device, file_path: str) -> bool: 24 | """Intenta hacer sideload del archivo especificado al dispositivo con visualización de progreso.""" 25 | if not os.path.isfile(file_path): 26 | print(f"[Error] El archivo '{file_path}' no existe.", file=sys.stderr) 27 | return False 28 | 29 | if not file_path.lower().endswith(('.apk', '.zip')): 30 | print(f"[Advertencia] El archivo '{file_path}' no es un archivo .apk o .zip.", file=sys.stderr) 31 | proceed = input("¿Continuar con el sideload? (s/n): ").strip().lower() 32 | if proceed != 's': 33 | print("[*] Sideload cancelado.") 34 | return False 35 | 36 | print(f"[*] Iniciando sideload de '{file_path}' al dispositivo {device.serial}...") 37 | try: 38 | # Construye el comando de sideload 39 | cmd = ["adb", "-s", device.serial, "sideload", file_path] 40 | process = subprocess.Popen( 41 | cmd, 42 | stdout=subprocess.PIPE, 43 | stderr=subprocess.PIPE, 44 | text=True, 45 | bufsize=1, # Búfer de línea 46 | universal_newlines=True 47 | ) 48 | 49 | # Expresión regular para capturar porcentajes (e.g., "~47%" o "47%") 50 | percentage_re = re.compile(r'~?(\d+)%') 51 | 52 | # Procesa la salida en tiempo real 53 | last_percentage = -1 54 | while True: 55 | line = process.stderr.readline() # adb sideload suele usar stderr 56 | if not line and process.poll() is not None: 57 | break 58 | if line: 59 | # Busca el porcentaje en la línea 60 | match = percentage_re.search(line) 61 | if match: 62 | percentage = int(match.group(1)) 63 | if percentage != last_percentage: # Evita actualizaciones redundantes 64 | print(f"\r[*] Progreso: {percentage}%", end="", flush=True) 65 | last_percentage = percentage 66 | else: 67 | # Imprime otras líneas relevantes (e.g., errores o estado final) 68 | if "Total xfer" in line or "error" in line.lower() or "failed" in line.lower(): 69 | print(f"\r{line.strip()}") 70 | 71 | # Asegura un salto de línea después del progreso 72 | if last_percentage >= 0: 73 | print() 74 | 75 | # Verifica el código de salida del proceso 76 | return_code = process.wait(timeout=300) # Timeout de 5 minutos 77 | if return_code == 0: 78 | print("[*] Sideload completado exitosamente.") 79 | return True 80 | else: 81 | # Captura cualquier salida de error restante 82 | remaining_output = process.stderr.read() 83 | if remaining_output: 84 | print(f"[Error] Fallo en el sideload: {remaining_output.strip()}", file=sys.stderr) 85 | else: 86 | print("[Error] Fallo en el sideload con error desconocido.", file=sys.stderr) 87 | return False 88 | 89 | except subprocess.TimeoutExpired: 90 | print("[Error] El sideload excedió el tiempo límite de 5 minutos.", file=sys.stderr) 91 | process.terminate() 92 | return False 93 | except FileNotFoundError: 94 | print("[Error] Ejecutable ADB no encontrado. Asegúrate de que 'adb' esté en tu PATH.", file=sys.stderr) 95 | return False 96 | except Exception as e: 97 | print(f"[Error] Fallo en el sideload: {e}", file=sys.stderr) 98 | return False 99 | 100 | def sideload_loop(device: Device): 101 | """Ejecuta el bucle de sideload para el dispositivo seleccionado.""" 102 | limpiar_pantalla() 103 | serial = device.serial 104 | print(f"--- ADB Sideload para {serial} ---") 105 | print("Comandos: 'select' para elegir un archivo, 'exit' o 'quit' para volver al terminal principal.") 106 | print("O proporciona la ruta completa a un archivo .apk o .zip para sideload.") 107 | print("-" * (len(serial) + 30)) 108 | 109 | while True: 110 | try: 111 | prompt = f"sideload@{serial}> " 112 | user_input = input(prompt).strip() 113 | 114 | if not user_input: 115 | continue 116 | 117 | if user_input.lower() in ['exit', 'quit']: 118 | print("Volviendo al terminal principal...") 119 | break 120 | 121 | if user_input.lower() == 'select': 122 | file_path = select_file_with_tkinter() 123 | if file_path: 124 | print(f"[*] Archivo seleccionado: {file_path}") 125 | sideload_file(device, file_path) 126 | else: 127 | print("[*] No se seleccionó ningún archivo.") 128 | continue 129 | 130 | # Asume que la entrada es una ruta de archivo 131 | file_path = user_input.replace('"', '').replace("'", '').strip() 132 | sideload_file(device, file_path) 133 | 134 | except KeyboardInterrupt: 135 | print("\nCapturado Ctrl+C. Escribe 'exit' o 'quit' para volver al terminal principal.") 136 | except EOFError: 137 | print("\nEOF detectado. Volviendo al terminal principal.") 138 | break 139 | except Exception as loop_error: 140 | print(f"\n[Error en el Bucle de Sideload] Ocurrió un error inesperado: {loop_error}", file=sys.stderr) 141 | print("Volviendo al terminal principal.", file=sys.stderr) 142 | break 143 | 144 | limpiar_pantalla() 145 | 146 | def main(device: Device): 147 | """Función principal del módulo de sideload, invocable desde otros scripts.""" 148 | try: 149 | sideload_loop(device) 150 | except KeyboardInterrupt: 151 | print("\n[*] Sideload ADB interrumpido por el usuario.") 152 | limpiar_pantalla() 153 | except Exception as e: 154 | print(f"[Error] Sideload: {e}", file=sys.stderr) 155 | limpiar_pantalla() -------------------------------------------------------------------------------- /scripts/terminal.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | from ppadb.client import Client as AdbClient 5 | from ppadb.device import Device 6 | 7 | def limpiar_pantalla(): 8 | """Clears the terminal screen.""" 9 | os.system("cls" if os.name == "nt" else "clear") 10 | 11 | def conectar_y_listar_dispositivos(): 12 | """Connects to the ADB server and returns a list of devices.""" 13 | try: 14 | client = AdbClient(host="127.0.0.1", port=5037) 15 | devices = client.devices() 16 | return devices 17 | except RuntimeError as e: 18 | print(f"[Error] Could not connect to ADB server: {e}", file=sys.stderr) 19 | print("Please ensure the ADB server is running ('adb start-server').", file=sys.stderr) 20 | return None 21 | except Exception as e: 22 | print(f"[Error] An unexpected error occurred connecting to ADB: {e}", file=sys.stderr) 23 | return None 24 | 25 | def seleccionar_dispositivo(devices: list) -> Device | None: 26 | """Prompts the user to select a device from the list.""" 27 | if not devices: 28 | print("[Error] No ADB devices found.", file=sys.stderr) 29 | print("Make sure your device is connected and USB Debugging is enabled.", file=sys.stderr) 30 | return None 31 | 32 | if len(devices) == 1: 33 | print(f"[*] Auto-selecting device: {devices[0].serial}") 34 | return devices[0] 35 | 36 | print("Available devices:") 37 | for i, device in enumerate(devices): 38 | try: 39 | model = device.shell("getprop ro.product.model").strip() 40 | print(f" {i + 1}. {device.serial} ({model})") 41 | except Exception: 42 | print(f" {i + 1}. {device.serial}") 43 | 44 | while True: 45 | try: 46 | choice = input("Select a device number to use: ").strip() 47 | if not choice: 48 | print("[Error] No selection made.", file=sys.stderr) 49 | return None 50 | 51 | device_index = int(choice) - 1 52 | if 0 <= device_index < len(devices): 53 | selected_device = devices[device_index] 54 | print(f"[*] Using device: {selected_device.serial}") 55 | return selected_device 56 | else: 57 | print(f"[Error] Invalid choice. Please enter a number between 1 and {len(devices)}.", file=sys.stderr) 58 | except ValueError: 59 | print("[Error] Invalid input. Please enter a number.", file=sys.stderr) 60 | except Exception as e: 61 | print(f"[Error] An error occurred during selection: {e}", file=sys.stderr) 62 | return None 63 | 64 | def terminal_loop(device: Device): 65 | """Runs the main command loop for the selected device.""" 66 | limpiar_pantalla() 67 | serial = device.serial 68 | print(f"--- ADB Connected to {serial} ---") 69 | print("Type 'exit' or 'quit' to disconnect.") 70 | print("-" * (len(serial) + 30)) 71 | 72 | while True: 73 | try: 74 | prompt = f"adb@{serial}> " 75 | command = input(prompt).strip() 76 | 77 | if not command: 78 | continue 79 | 80 | if command.lower() in ['exit', 'quit']: 81 | print("Disconnecting...") 82 | break 83 | 84 | try: 85 | result = device.shell(command, timeout=30) 86 | print(result) 87 | except Exception as cmd_error: 88 | print(f"[Command Error] {cmd_error}", file=sys.stderr) 89 | 90 | except KeyboardInterrupt: 91 | print("\nCaught Ctrl+C. Type 'exit' or 'quit' to disconnect.") 92 | except EOFError: 93 | print("\nEOF detected. Exiting.") 94 | break 95 | except Exception as loop_error: 96 | print(f"\n[Terminal Loop Error] An unexpected error occurred: {loop_error}", file=sys.stderr) 97 | print("Exiting terminal.", file=sys.stderr) 98 | break 99 | 100 | print("\n" * (os.get_terminal_size().lines - 3)) 101 | print("---By 3v0lv3d---") 102 | 103 | # --- Función reutilizable para importar --- 104 | def main(device: Device): 105 | """Función principal exportable para integrarla en otros scripts.""" 106 | try: 107 | terminal_loop(device) 108 | except KeyboardInterrupt: 109 | print("\n[*] Terminal ADB interrumpida por el usuario.") 110 | except Exception as e: 111 | print(f"[Error] Terminal: {e}", file=sys.stderr) 112 | 113 | # --- Ejecución directa desde terminal --- 114 | if __name__ == "__main__": 115 | print("[*] Initializing ADB Terminal...") 116 | connected_devices = conectar_y_listar_dispositivos() 117 | 118 | if connected_devices is None: 119 | sys.exit(1) 120 | 121 | selected_device = seleccionar_dispositivo(connected_devices) 122 | 123 | if selected_device: 124 | main(selected_device) 125 | else: 126 | print("[*] No device selected. Exiting.") 127 | sys.exit(1) 128 | --------------------------------------------------------------------------------