Files
Bose-SoundTouch/docs/analysis/BOSE-APP-ADB-Emulator.md
T

9.5 KiB

Bose SoundTouch Traffic Interception Runbook

Intercept HTTPS/WebSocket traffic from the Bose SoundTouch Android app using an Android emulator, mitmproxy, and Frida. Tested on Apple Silicon (ARM64) Mac.

Prerequisites

  • Android Studio installed (for SDK tools and emulator)
  • Docker installed
  • mitmproxy installed (pip install mitmproxy or via your preferred method)
  • The Bose SoundTouch APK (extracted from a real device, see below)

Add Android SDK tools to your PATH (add to ~/.zshrc):

export PATH=$PATH:~/Library/Android/sdk/emulator
export PATH=$PATH:~/Library/Android/sdk/platform-tools

1. Extract APK from Real Device

Connect your Android device via USB with USB debugging enabled.

adb devices
# note your device ID, e.g. "ABC123"

adb -s ABC123 shell pm path com.bose.soundtouch
# output e.g.: package:/data/app/~~xyz/com.bose.soundtouch-abc/base.apk

adb -s ABC123 pull /data/app/~~xyz/com.bose.soundtouch-abc/base.apk bose.apk

2. Create Android Emulator (ARM64, API 33)

On Apple Silicon you need an ARM64 image. Use the avdmanager and sdkmanager CLI tools.

# Install the system image
~/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager \
  "system-images;android-33;google_apis;arm64-v8a"

# Create the AVD
~/Library/Android/sdk/cmdline-tools/latest/bin/avdmanager create avd \
  -n Pixel_6_API33 \
  -k "system-images;android-33;google_apis;arm64-v8a" \
  -d "pixel_6"

Alternatively create the AVD via Android Studio Device Manager (choose "Google APIs", arm64-v8a, API 33).


3. Start Emulator with Writable System

# List available AVDs
~/Library/Android/sdk/emulator/emulator -list-avds

# Start with writable system partition
~/Library/Android/sdk/emulator/emulator -avd Pixel_6_API33 -writable-system

Wait until the emulator has fully booted, then:

adb -s emulator-5554 root
adb -s emulator-5554 shell avbctl disable-verification
adb -s emulator-5554 reboot

# After reboot:
adb -s emulator-5554 root

4. Install Bose APK

adb -s emulator-5554 install bose.apk

5. Set Up mitmproxy

# Start mitmproxy (generates CA cert on first run)
mitmweb --port 8080 --mode regular -w bose_traffic.mitm

Extract the CA certificate (without private key):

openssl x509 -in ~/.mitmproxy/mitmproxy-ca.pem -out ~/.mitmproxy/mitmproxy-ca-cert.pem

# Verify it's the mitmproxy cert, not another cert:
openssl x509 -in ~/.mitmproxy/mitmproxy-ca-cert.pem -noout -issuer
# should show: issuer= /CN=mitmproxy/O=mitmproxy

6. Install mitmproxy CA Certificate in Emulator

HASH=$(openssl x509 -inform PEM -subject_hash_old \
  -in ~/.mitmproxy/mitmproxy-ca-cert.pem | head -1)

adb -s emulator-5554 push ~/.mitmproxy/mitmproxy-ca-cert.pem /data/local/tmp/mitmproxy.pem

adb -s emulator-5554 shell su 0 mkdir -p /data/misc/user/0/cacerts-added

adb -s emulator-5554 shell su 0 \
  cp /data/local/tmp/mitmproxy.pem /data/misc/user/0/cacerts-added/${HASH}.0

adb -s emulator-5554 shell su 0 \
  chmod 644 /data/misc/user/0/cacerts-added/${HASH}.0

7. Set System Proxy in Emulator

Find your Mac's local IP:

ipconfig getifaddr en0
# e.g. 192.168.1.123

Set the proxy:

adb -s emulator-5554 shell settings put global http_proxy 192.168.1.123:8080

8. Set Up Frida (via Python venv)

python3 -m venv /tmp/frida-venv
/tmp/frida-venv/bin/pip install frida==17.9.1 frida-tools==14.8.1

Download the frida-server binary for ARM64 Android:

FRIDA_VERSION=17.9.1

curl -L "https://github.com/frida/frida/releases/download/${FRIDA_VERSION}/frida-server-${FRIDA_VERSION}-android-arm64.xz" \
  -o /tmp/frida-server.xz

unxz /tmp/frida-server.xz
mv /tmp/frida-server-${FRIDA_VERSION}-android-arm64 /tmp/frida-server

Push to emulator and start:

adb -s emulator-5554 push /tmp/frida-server /data/local/tmp/frida-server
adb -s emulator-5554 shell su 0 chmod 755 /data/local/tmp/frida-server
adb -s emulator-5554 shell su 0 /data/local/tmp/frida-server &

9. Download SSL Bypass Scripts

BASE=https://raw.githubusercontent.com/httptoolkit/frida-interception-and-unpinning/main

curl -L "${BASE}/config.js" -o /tmp/config.js
curl -L "${BASE}/android/android-system-certificate-injection.js" \
  -o /tmp/android-system-certificate-injection.js
curl -L "${BASE}/android/android-proxy-override.js" \
  -o /tmp/android-proxy-override.js
curl -L "${BASE}/android/android-certificate-unpinning.js" \
  -o /tmp/android-certificate-unpinning.js
curl -L "${BASE}/android/android-certificate-unpinning-fallback.js" \
  -o /tmp/android-certificate-unpinning-fallback.js

10. Configure config.js

Edit /tmp/config.js and set:

const CERT_PEM = `<contents of ~/.mitmproxy/mitmproxy-ca-cert.pem>`;

const PROXY_HOST = '192.168.1.123';  // your Mac IP
const PROXY_PORT = 8080;

Insert the full PEM content (from -----BEGIN CERTIFICATE----- to -----END CERTIFICATE-----) between the backticks.

Quick check that the right cert is in place:

# The issuer inside config.js should be mitmproxy, not SoundTouch
grep -A3 "CERT_PEM" /tmp/config.js | head -5

11. Start Interception

Make sure mitmweb is running, then:

/tmp/frida-venv/bin/frida \
  -U \
  -f com.bose.soundtouch \
  -l /tmp/config.js \
  -l /tmp/android-system-certificate-injection.js \
  -l /tmp/android-proxy-override.js \
  -l /tmp/android-certificate-unpinning.js \
  -l /tmp/android-certificate-unpinning-fallback.js

Expected output in the Frida REPL:

== System certificate trust injected ==
== Proxy system configuration overridden to 192.168.1.123:8080 ==
== Proxy configuration overridden to 192.168.1.123:8080 ==
== Certificate unpinning completed ==
== Unpinning fallback auto-patcher installed ==

Open mitmweb at http://127.0.0.1:8081 to observe traffic live.


12. Save & Replay Recordings

Traffic is saved to bose_traffic.mitm (set via -w flag in step 5).

# Replay/analyse a saved recording:
mitmweb -r bose_traffic.mitm

Cleanup

# Remove proxy setting from emulator
adb -s emulator-5554 shell settings delete global http_proxy

# Remove venv
rm -rf /tmp/frida-venv /tmp/frida-server /tmp/frida-server.xz
rm /tmp/config.js /tmp/android-*.js

# Stop emulator
adb -s emulator-5554 emu kill

Troubleshooting

Symptom Cause Fix
remount failed ARM64 emulator doesn't support overlayfs remount Use /data/misc/user/0/cacerts-added/ method instead
TLS: Trust anchor not found Wrong certificate in config.js Check issuer: must be mitmproxy, not SoundTouch
Chain validation failed Private key included in cert Re-extract with openssl x509 -in mitmproxy-ca.pem -out mitmproxy-ca-cert.pem
frida-server: connection refused frida-server not running Re-run adb shell su 0 /data/local/tmp/frida-server &
frida and frida-server version mismatch Versions must be identical Pin both to same version (e.g. 17.9.1)
emulator: multiple AVDs error Emulator already running Kill first: adb emu kill, then restart with -writable-system

App Automation Options

For most traffic-recording purposes, manually operating the app while mitmproxy captures is sufficient. If you need to automate specific interactions (e.g. to repeatably capture the requests triggered by startup or a particular action), the following tools are available.

Starting the App

# Via app drawer: swipe up on the home screen and tap "Bose SoundTouch"

# Via adb monkey (simplest)
adb -s emulator-5554 shell monkey -p com.bose.soundtouch 1

# Via explicit intent (if the activity name is known)
adb -s emulator-5554 shell am start -n com.bose.soundtouch/.MainActivity

# Look up all activities if the name is unknown
adb -s emulator-5554 shell dumpsys package com.bose.soundtouch | grep Activity

adb — sufficient for simple cases

# Tap at screen coordinates
adb shell input tap 540 960

# Swipe
adb shell input swipe 540 1500 540 500

# Type text
adb shell input text "mytext"

# Take a screenshot
adb shell screencap /sdcard/screen.png && adb pull /sdcard/screen.png

UIAutomator2 — inspect UI elements

# Dump the current UI hierarchy to find element IDs
adb shell uiautomator dump /sdcard/ui.xml
adb pull /sdcard/ui.xml

Open ui.xml to find element resource IDs, then target them precisely in scripts.

Appium — full scripted automation

from appium import webdriver

driver = webdriver.Remote('http://localhost:4723/wd/hub', {
    'platformName': 'Android',
    'appPackage': 'com.bose.soundtouch',
    'appActivity': '.MainActivity',
})

# Find an element by resource ID and tap it
driver.find_element('id', 'com.bose.soundtouch:id/play_button').click()

Note: monkey is a stress-test tool that sends random events — use it only to launch the app, not to drive specific interactions.