Logo del proyecto fl3xbl0w

Respaldar los contenidos de tu Consola - fl3xbl0w

Lanzado el 28 may 2022

Proyecto de ingeniería inversa. Comenzó con la trotadora Bowflex Treadmill 22 pero terminó generalizándose para cualquier máquina con Android vendida por Nautilus Inc. (Nautilus, Bowflex, Schwinn).

Después de ser liberado de AppMonitorService (o después de recibir cualquier actualización OTA), lo primero que hay que hacer es respaldar la mayor cantidad de datos posible.

Asegúrate de estar ya conectado a la consola:

> adb connect <Dirección IP de la Consola>

Respaldo del contenido de /sdcard

Puede contener algunos archivos necesarios para que la máquina funcione correctamente después de un restablecimiento de fábrica (principalmente en las carpetas /sdcard/Android y /sdcard/Nautilus). Respaldaremos todo con:

> adb pull /sdcard/ .

Respaldo de APKs

Primero, obtén la lista de todos los paquetes instalados en la Consola (solo para que tengas una referencia):

> adb shell pm list packages -f

Aparecerán en el siguiente formato: package:<ubicación del APK>=<nombre del paquete>

Tendrás que encontrar las aplicaciones que quieres respaldar. NO TODAS LAS APLICACIONES SON NECESARIAS, la mayoría son aplicaciones estándar de Android, así que solo buscaremos “aplicaciones no estándar” mediante algunos filtros:

> adb shell pm list packages -f | grep -wviE 'com.android|com.google|framework-res|/vendor/overlay'

Podrás ver registros de aplicaciones de com.nautilus, com.redbend, com.netflix, com.amazon, com.disney y quizás algunas más en el futuro. Querremos respaldar todo lo que veamos en esa lista (ID únicos ocultos, usa tus propios resultados):

> adb shell pm list packages -f | grep -wviE 'com.android|com.google|framework-res|/vendor/overlay'
package:/data/app/com.nautilus.sbctest-XXXXXXXXXXXXXXXX/base.apk=com.nautilus.sbctest
package:/data/app/com.redbend.client-XXXXXXXXXXXXXXXX/base.apk=com.redbend.client
package:/data/app/com.nautilus.nlssbcsystemsettings-XXXXXXXXXXXXXXXX/base.apk=com.nautilus.nlssbcsystemsettings
package:/system/priv-app/RBDualPartService/RBDualPartService.apk=com.redbend.dualpart.service.app
package:/data/app/com.netflix.mediaclient-XXXXXXXXXXXXXXXX/base.apk=com.netflix.mediaclient
package:/data/app/com.nautilus.nautiluslauncher-XXXXXXXXXXXXXXXX/base.apk=com.nautilus.nautiluslauncher
package:/data/app/com.amazon.avod.thirdpartyclient-XXXXXXXXXXXXXXXX/base.apk=com.amazon.avod.thirdpartyclient
package:/data/app/com.nautilus.sbc_demo_app-XXXXXXXXXXXXXXXX/base.apk=com.nautilus.sbc_demo_app
package:/data/app/com.nautilus.UtilityApp-XXXXXXXXXXXXXXXX/base.apk=com.nautilus.UtilityApp
package:/data/app/com.nautilus.g4assetmanager-XXXXXXXXXXXXXXXX/base.apk=com.nautilus.g4assetmanager
package:/data/app/com.nautilus.platform_hardwaretest-XXXXXXXXXXXXXXXX/base.apk=com.nautilus.platform_hardwaretest
package:/data/app/com.nautilus.webviewer-XXXXXXXXXXXXXXXX/base.apk=com.nautilus.webviewer
package:/data/app/com.nautilus.bowflex.usb-XXXXXXXXXXXXXXXX/base.apk=com.nautilus.bowflex.usb
package:/data/app/com.disney.disneyplus-XXXXXXXXXXXXXXXX/base.apk=com.disney.disneyplus

NOTA: com.redbend.dualpart.service.app proviene de /system/priv-app/, lo que significa que permanecerá instalado incluso después de un restablecimiento de fábrica. Podemos respaldarla de todas formas si te interesa la ingeniería inversa de las aplicaciones.

Tomemos Disney Plus como ejemplo (ID único oculto)

package:/data/app/com.disney.disneyplus-XXXXXXXXXXXXXXXX/base.apk=com.disney.disneyplus

Esa línea, basada en el formato que he mencionado, sería:

Ubicación del paquete: /data/app/com.disney.disneyplus-XXXXXXXXXXXXXXXX/base.apk
Nombre del paquete: com.disney.disneyplus

Buscaremos qué versión de la aplicación tenemos con esa información. Usemos el Nombre del paquete que acabamos de identificar y ejecutemos:

> adb shell dumpsys package com.disney.disneyplus | grep versionName

En mi caso, he recibido:

> adb shell dumpsys package com.disney.disneyplus | grep versionName
    versionName=2.4.2-rc2

Ahora, para crear un respaldo para el paquete de Disney Plus, el procedimiento sería:

> adb pull /data/app/com.disney.disneyplus-XXXXXXXXXXXXXXXX/base.apk .

Inmediatamente después de que termine, ve a tu Explorador de Archivos y cambia el nombre del archivo base.apk recién respaldado a com.disney.disneyplus-2.4.2-rc2.apk.

El formato que sugiero para los APKs, dado ese ejemplo, es: <Nombre del paquete>-<versión>.apk

Ahora repite el proceso para el resto de las aplicaciones.

Respaldo de AppData

Parece ser capaz de respaldar algunos (no estoy seguro si todos) los datos de las aplicaciones.

Ten en cuenta que no todas las aplicaciones instaladas generan datos de aplicaciones, o nosotros, como el usuario shell, es posible que no tengamos permisos para respaldar todo.

Crea un respaldo completo de todas las aplicaciones instaladas por el usuario:

> adb backup -f appdata.adb -all -noapk -nosystem

Alternativamente, puedes obtener el mismo resultado de un comando alternativo:

> adb shell 'bu backup -all -noapk -nosystem' > appdata.adb

Ambos comandos solicitarán una “confirmación en pantalla” de que quieres hacer un respaldo. Toca en “BACK UP MY DATA”:

pantalla de confirmación de respaldo

Si quieres extraer los contenidos (en Linux, macOS), necesitarás zlib-flate de qpdf y ejecutar:

> dd if=appdata.adb bs=24 skip=1 | zlib-flate -uncompress | tar xf -

Hay algunos datos fascinantes para com.nautilus.bowflex.usb. Hay información personal identificable (PII) dentro, así que asegúrate de revisar bien qué archivos compartes.

Estos métodos de respaldo y extracción provienen de este Gist. Solo he probado los métodos de respaldo listados allí, que son seguros para jugar. Si juegas con la Restauración del Gist, estás por tu cuenta. ¡Avísanos si funciona!

Script

Desarrollé un script rápido y sucio de Python para respaldar algunos contenidos (por ahora no incluye AppData). Solo probado en macOS con android-platform-tools instalado a través de brew. Lee el script y asegúrate de que tenga sentido para ti antes de ejecutarlo.

import sys
import os
import subprocess
from datetime import datetime

# verificar si se dio un argumento
if len(sys.argv) != 2:
    print("Uso: python3 dump.py <Dirección IP de la Consola>")
    sys.exit(1)

ipaddr = sys.argv[1]

# verificar si adb está disponible para shell
adb_path = subprocess.check_output(["which", "adb"]).decode("utf-8").strip()
if not os.path.exists(adb_path):
    print("adb no encontrado")
    exit()

# configurar carpeta de respaldo con fecha y hora actual
dump_folder = os.path.join(
    os.environ["HOME"],
    "Desktop",
    "nautilus_dump",
    "{}".format(datetime.now().strftime("%Y-%m-%d_%H-%M-%S")),
)
if not os.path.exists(dump_folder):
    os.makedirs(dump_folder)

# conectar adb
print("Conectando a {}...".format(ipaddr))
subprocess.call(["adb connect {}".format(ipaddr)], shell=True)

# respaldar contenido de /sdcard
print("Respaldando contenido de /sdcard...")
subprocess.call(["adb pull /sdcard/ {}".format(dump_folder)], shell=True)

# obtener información de APK
print("Respaldando APKs...")
packages = (
    subprocess.check_output(["adb shell pm list packages -f"], shell=True)
    .decode("utf-8")
    .split("\n")
)

# filtrar líneas vacías
packages = [x for x in packages if x.strip()]

# filtrar líneas que contienen "com.android", "com.google", "framework-res"
packages = [package for package in packages if package.find("com.android") == -1]
packages = [package for package in packages if package.find("com.google") == -1]
packages = [package for package in packages if package.find("framework-res") == -1]

print("Encontrados {} APKs".format(len(packages)))

for package in packages:
    package_name = package.split("=")
    package_name = package_name[len(package_name) - 1].strip()
    package_version = (
        subprocess.check_output(
            ["adb shell dumpsys package {} | grep versionName".format(package_name)],
            shell=True,
        )
        .decode("utf-8")
        .split("=")[1]
        .strip()
    )

    package_path = package.split(":")[1].split(".apk=")[0].strip()
    package_path = "{}.apk".format(package_path)
    print("Respaldando {} v{}...".format(package_name, package_version))

    subprocess.call(["adb pull {} {}".format(package_path, dump_folder)], shell=True)

    os.rename(
        os.path.join(dump_folder, os.path.basename(package_path)),
        os.path.join(dump_folder, "{}-{}.apk".format(package_name, package_version)),
    )

# respaldar appdata
print("Respaldando appdata.adb ...")
print('¡TOCA EL BOTÓN "RESPALDAR MIS DATOS" EN PANTALLA AHORA !!!')
subprocess.call(
    ["adb backup -f {}/appdata.adb -all -noapk -nosystem".format(dump_folder)],
    shell=True,
)

# desconectar adb
print("Desconectando...")
subprocess.check_output(["adb disconnect"], shell=True)

©2022-2024 Sebastián Barrenechea. Todos los derechos reservados.

Construido con Astro v4.15.9.