fl3xbl0w project logo

Backing Up the Contents of Your Console - fl3xbl0w

Released on May 28, 2022

Reverse engineering project. It started with the Bowflex Treadmill 22 but ended up being generalized for any Android machine sold by Nautilus Inc. (Nautilus, Bowflex, Schwinn).

After being released from AppMonitorService (or after receiving any OTA update), the first thing to do is to back up as much data as possible.

Make sure you are already connected to the console:

> adb connect <Console IP Address>

Backup of /sdcard content

It may contain some files necessary for the machine to work properly after a factory reset (mainly in the /sdcard/Android and /sdcard/Nautilus folders). We will back up everything with:

> adb pull /sdcard/ .

APK Backup

First, get the list of all the packages installed on the Console (just so you have a reference):

> adb shell pm list packages -f

They will appear in the following format: package:<APK location>=<package name>

You will have to find the applications you want to back up. NOT ALL APPLICATIONS ARE NECESSARY, most are standard Android applications, so we will only look for “non-standard applications” using some filters:

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

You will see records of applications from com.nautilus, com.redbend, com.netflix, com.amazon, com.disney, and perhaps some more in the future. We will want to back up everything we see on that list (hidden unique IDs, use your own results):

> 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

NOTE: com.redbend.dualpart.service.app comes from /system/priv-app/, which means it will remain installed even after a factory reset. We can back it up anyway if you are interested in reverse engineering the applications.

Let’s take Disney Plus as an example (hidden unique ID)

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

That line, based on the format I’ve mentioned, would be:

Package location: /data/app/com.disney.disneyplus-XXXXXXXXXXXXXXXX/base.apk
Package name: com.disney.disneyplus

We will look for what version of the application we have with that information. Let’s use the Package name we just identified and run:

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

In my case, I received:

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

Now, to create a backup for the Disney Plus package, the procedure would be:

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

Immediately after it finishes, go to your File Explorer and rename the newly backed up base.apk file to com.disney.disneyplus-2.4.2-rc2.apk.

The format I suggest for APKs, given that example, is: <Package name>-<version>.apk

Now repeat the process for the rest of the applications.

AppData Backup

It seems to be able to back up some (I’m not sure if all) application data.

Keep in mind that not all installed applications generate application data, or we, as the shell user, may not have permissions to back up everything.

Create a full backup of all user-installed applications:

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

Alternatively, you can get the same result from an alternative command:

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

Both commands will request an “on-screen confirmation” that you want to make a backup. Tap on “BACK UP MY DATA”:

backup confirmation screen

If you want to extract the contents (on Linux, macOS), you will need zlib-flate from qpdf and run:

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

There is some fascinating data for com.nautilus.bowflex.usb. There is personally identifiable information (PII) inside, so make sure to check well what files you share.

These backup and extraction methods come from this Gist. I have only tested the backup methods listed there, which are safe to play with. If you play with the Restoration from the Gist, you’re on your own. Let us know if it works!

Script

I developed a quick and dirty Python script to back up some contents (it does not include AppData for now). Only tested on macOS with android-platform-tools installed via brew. Read the script and make sure it makes sense to you before running it.

import sys
import os
import subprocess
from datetime import datetime

# check if an argument was given
if len(sys.argv) != 2:
    print("Usage: python3 dump.py <Console IP Address>")
    sys.exit(1)

ipaddr = sys.argv[1]

# check if adb is available for shell
adb_path = subprocess.check_output(["which", "adb"]).decode("utf-8").strip()
if not os.path.exists(adb_path):
    print("adb not found")
    exit()

# set up backup folder with current date and time
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)

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

# backup content of /sdcard
print("Backing up content of /sdcard...")
subprocess.call(["adb pull /sdcard/ {}".format(dump_folder)], shell=True)

# get APK information
print("Backing up APKs...")
packages = (
    subprocess.check_output(["adb shell pm list packages -f"], shell=True)
    .decode("utf-8")
    .split("\n")
)

# filter out empty lines
packages = [x for x in packages if x.strip()]

# filter out lines containing "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("Found {} 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("Backing up {} 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)),
    )

# backup appdata
print("Backing up appdata.adb ...")
print('TAP THE "BACK UP MY DATA" BUTTON ON SCREEN NOW !!!')
subprocess.call(
    ["adb backup -f {}/appdata.adb -all -noapk -nosystem".format(dump_folder)],
    shell=True,
)

# disconnect adb
print("Disconnecting...")
subprocess.check_output(["adb disconnect"], shell=True)
Content translated by gpt-4-1106-preview

©2022-2024 Sebastian Barrenechea. All rights reserved.

Built with Astro v4.15.9.