Reverse engineering the MyBMW API

This document should be seen as a help in setting up a working environment to intercept traffic of the MyBMW app. Not every step will be described fully, this guide is rather a summary and list for further reading. It will most likely need adjustments to your specific setup.

The MyBMW app is built with the Flutter framework and needs some additional persuasion to reveal the traffic.

Disclaimer

Note that we are actively disabling important security measures such as SSL/TLS encryption to understand which commands and messages are shared between the MyBMW app and the MyBMW servers.

Also note that there could always be changes to the API or the app itself made by BMW to stop us from understanding what is going on.

Acknowledgement

Most of this document would not exist without the amazing work of Jeroen Becker:

Software & hardware requirements

Note

This document is based on the MyBMW Android app. It should work similarly using iPhones. If possible, please create a PR with more details.

You will need:

  • A proxy with MITM capabilities such as mitmproxy

  • A rooted android phone with a version supported by MyBMW (currently Android 6.0 Marshmallow). It could also work using an Android emulator.

  • Access to your phone using ADB (via USB)

  • ProxyDroid to forward all traffic to your proxy

  • Ghidra to find the location to patch out SSL verification

  • A python environment with frida

  • frida-android-helper to help installing frida on your phone

Finding the location of SSL verification

The following steps are required if the location of the SSL verification function is not known. If it is, please continue with the next section. For more details, please refer to Jeroen Becker’s work.

Get an APK/XAPK of the MyBMW app (from your phone or one of the many download sites). APK names include:

  • de.bmw.connected.mobile20.cn (china)

  • de.bmw.connected.mobile20.na (north america)

  • de.bmw.connected.mobile20.row (rest of world)

Now extract config.arm64-v8a.apk or config.armeabi-v7a.apk from the APK package (depending of your phone’s target architecture).

In Ghidra, load and analyze lib/ARCH/libflutter.so.

After analyze has finished, go to Search > For Scalar and search for value 390. Find mov r3, #0x186 and jump to it.

Double click on function name on right side to get the hex address and first bytes of the function

Example: 2d e9 f0 4f a3 b0 81 46 50 20 10 70

Preparations on phone

On your phone, add your custom CA certificates to the system store (instructions for emulator, but works on rooted devies in similar fashion). This is required as the login screen is using the default Android WebView component, which again behaves differently from Flutter (or rather, behaves like expected).

Add your local proxy server to your Android system using ProxyDroid.

Disabling SSL verification with frida

Install & upgrade frida-tools & frida-android-helper (see requirements). Make sure that both are on the latest version.

Create a frida hook named hook_flutter_disable_ssl.js with the following content. If needed, replace the search pattern and disable adding 0x01 on ARMv8.

function hook_ssl_verify_result(address)
{
  Interceptor.attach(address, {
    onEnter: function(args) {
      console.log("Disabling SSL validation")
    },
    onLeave: function(retval)
    {
      console.log("Retval: " + retval)
      retval.replace(0x1);

    }
  });
}
function disablePinning()
{
var m = Process.findModuleByName("libflutter.so");
var pattern = "2d e9 f0 4f a3 b0 81 46 50 20 10 70" // MyBMW 1.5.1 to 1.7.0 (all regions)


var res = Memory.scan(m.base, m.size, pattern, {
  onMatch: function(address, size){
      console.log('[+] ssl_verify_result found at: ' + address.toString());

      // Add 0x01 because it's a THUMB function
      // Otherwise, we would get 'Error: unable to intercept function at 0x9906f8ac; please file a bug'
      // REQUIRED ON ARMv7 ONLY!!
      hook_ssl_verify_result(address.add(0x01));

    },
  onError: function(reason){
      console.log('[!] There was an error scanning memory');
    },
    onComplete: function()
    {
      console.log("All done")
    }
  });
}
setTimeout(disablePinning, 1000)

Connect to your phone via ADB with root permissions.

adb root && adb remount

Update & start frida server on the phone with frida-android-helper.

fah server update && fah server start

Start the MyBMW app from your computer via frida (adjust app identifier if needed).

frida -Uf de.bmw.connected.mobile20.row -l .\hook_flutter_disable_ssl.js --no-pause

Now you should be able to capture all traffic between your phone and the MyBMW API.

Using the information in bimmer_connected

If you learn anything by capturing the traffic, please create Issues/Feature Requests or Pull Requests to our repository. Information that should be included contains:

  • The URL of the endpoint

  • HTTP headers of your request (DO NOT include Cookie or Authentication headers)

  • The request payload (if available)

  • The request response (if available)

If the data contains personal information, please do not delete it but replace it with random data.

Warning

Double check if all information is sanitized and no personal information or authentication data is included.