Giter Club home page Giter Club logo

bmw's Introduction

BMW Connected Drive SDK & CLI

BMW vehicles offer limited control and telemetry through the MyBMW app. This library uses the private APIs to enable scipting and integrations with third party tools.

Quick Start

  • npm install -g https://github.com/colinbendell/bmw
  • Configuration #authentication
  • Use the bmw CLI

Authentication

Authentication uses the MyBMW account credentials. It can be passed through to the library using one of three ways:

  • environment variables BMW_EMAIL, BMW_PASSWORD and BMW_GEO

  • config file located ~/.bmw in the form of:

    [default]
    email[email protected]
    password=p@$$w0rd
    geo=na
  • pass the values directly into the constructor new BMWClientAPI(username, password, geo)

The valid geo values are: na (North America), cn (China) and row (Rest of World).

NB: na is the only one that has been really tested so far

Command Line Interface

The ./bmw provides a number of commands to invoke the APIs through a shell script. Use bmw --help for expanded help details.

The available commands include:

  • bmw login to test the auth credentials
  • bmw charge [vin] to start charging the car
  • bmw climate [vin] to start charging the car
  • bmw lock [vin] to lock the vehicle
  • bmw unlock [vin] to unlock a specific vehicle
  • bmw flash [vin] to flash the lights
  • bmw honk [vin] to unlock the vehicle
  • bmw list list the vehicles associated with the account
  • bmw info [vin] current status info of the vehicle
  • bmw status [vin] build and configuration info of the vehicle
  • bmw trips [vin] list trip information for a given month
  • bmw charging [vin] log of the charges for the vehicle

The [vin] is optional in all cases. If absent, all vehicles are used.

Protip: You can use parts of the model name instead of the [vin]. Eg: bmw status iX

Library Overview

There are three main components to the library:

  • src/bmw-api.js - API wrapper library that manages authentication (see #auth)
  • src/bmw.js - the main business logic that wraps over the api calls
  • src/bmw-cli.js - a convenience CLI for scripting and automation

Debugging the MyBMW App

Extracting the current set of APIs is a bit of a challenge because the MyBMW app is built with Flutter. While Flutter is a convenient dev environment, it doesn't use the OS provided network libraries like nsURLSession or HttpClient. This would make it easier to trace the Network access and the API calls using a standard MitM tooling approaches (proxy + root CA). Instead, Flutter apps roll their own network stack which requires a lot more work to intercept the applications network calls.

The simplest approach is:

  1. Install Android Studio
  2. Create a Virtual Device in the VDM (Virtual Device Manager). Currently My BMW needs Android 31+
  3. Download the latest My BMW APK (either copy the installed APK from the play store install, or download from apkpure or other sources)
    • Use adb root then adb pull /data/app/<hash>/de.bmw.connected.mobile20.na-<hash>/base.apk de.bmw.connected.mobile20.na.apk
    • Downlaod from apkmirror.com or apkpure.com
  4. Install Frida: pip3 install frida-tools
  5. Use Gadget to wrap the APK:
    • Installing objection: pip3 install objection
    • You might need to add /build-tools to your PATH and install apktool (brew install apktool)
    • Run objection patchapk -s de.bmw.connected.mobile20.na.apk
    • Install the patched apk to the device
  6. Install Wireguard on the android device
  7. Install mitmproxy: brew install mitmproxy
    • Start mitm in wireguard mode: mitmdump --mode wireguard --showhost --flow-detail 3 cocoapi.bmwgroup.us
    • Install the wireguard profile as directed in the output
    • NB: mitm with wireguard in docker on mac doesn't work because of the virtualized network stack. Best to run it in a terminal or use an rpi
    • NB: current version of mitm assumes the 10.0.0/24 network is availble. Make sure your current network is on a different subnet
  8. Download the Frida script as per the Flutter instructions
  9. Intercept traffic
    • Connect to the wireguard server

      Not required, but to test that browser interception is working, visit http://mitm.it and install the rootCA using the instructions)

    • Launch the bmw app on the VMD. It will pause until you run frida
    • on the local terminal run frida with gadget: frida -U gadget -l disable-flutter-tls.js

Notes about BMW's API

The BMW app suffers from Conway's Law:

Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.

This is apparent in the way that the authentication system is bifurcated across 3 geos (na, cn and row) as well as the way it uses many different application paths for similar but discrete functions of the same component (eg: /eadrax-crccs/v1/vehicles/ vs /eadrax-chs/v2/charging-sessions vs /eadrax-cps/v2/vehicles).

Worse yet is the inconsistent use of ?query parameters vs /url path parameters, json body parameters and http headers. Take the VIN parameter as an example: for /charging-statistics?vin= it is a query parameter, for /vehicles/${vin}/start-charging and more bizarely for /vehicles/state it needs to be an http header bmw-vin:.

Other oddities to be aware of:

  • The ?date= parameter is only yyyy-dd formatted and not a real date for /v1/vehicles/sustainability/trips/history

  • The ?timezone= is also necessary because the ?date= field isn't a proper ISO8701 formatted date which would have provided the timezone relative offset

  • The ?date= parameter must be GMT relative with millisecond precision for /v2/charging-sessions

  • But only the month is actually used in the ?date= parameter as all sessions in that month will be provided

  • Unless of course if you add ?include_date_picker=true which will discregard the ?date= field and use the current month instead. There is no real value for the extra json values so just use ?include_date_picker=false.

  • Some APIs require a Global Catalog ID HTTP Header. Use x-gcid: ... for /v1/vehicles/sustainability/trips/history and bmw-gcis: ... for /v2/recalls. Why the inconsistency? What does this do?

  • The GCID values appear to matter. See the sourcecode for specific values for each API.

  • Some API teams clearly never read the JSON spec and redundantly annotate date/time fields with timeUnit values. eg: "start": { "time": "2022-12-23T03:44:42Z", "timeUnit": "ISO8601" }, instead of simply "start": "2022-12-23T03:44:42Z".

  • Sending commands uses a two step pattern of creating a POST action and then querying the end point. For example POST /v3/presentation/remote-commands/... then GET /v3/presentation/remote-commands/eventStatus?eventId=...

  • There are different Remote Command endpoints /eadrax-vrccs/v3/presentation/remote-commands, /eadrax-vrccs/v1/vehicles, /eadrax-r360/v1/event/execute, /eadrax-r360/v1/vehicle/addresses, etc

  • The mix of application paths eadrax-vrccs and eadrax-vrccs for the same set of remote commands is another example of different engineering teams with split ownership

  • auth is done with Microsoft's API Management Open Product (hence the ocp-apim-subscription-key in the oauth dance)

  • There are multiple session tokens used bmw-session-id for most api contexts, and GCDMSSO Cookie for auth status.

  • This is in addition to valid Authorization: Bearer oauth api tokens.

  • It's unclear why bmw requires a session context when they provide oauth keys. I suspect that there are 3 different infrastructur teams at play managing server affinity, authorization and other contexts.

  • Many of the APIs also do content-negotiation through the use of HTTP headers. For example bmw-units-preferences: d=KM;v=L, country: uk, 24-hour-format: true.

  • Despite doing conneg with these HTTP headres, the response is missing Vary: to communicate the variances of the content

  • bmw-correlation-id and x-correlation-id appear to be OTP Spans for tracing. It's kind of silly really to have the same header twice

  • pagination sometimes uses ?offset= + ?limit= while other times uses ?next_token= and max_results=. (see: /eadrax-suscs/v1/vehicles/sustainability/trips/history and /eadrax-chs/v2/charging-sessions respectively)

  • naming conventions are very inconsistent. sometimes it's snake_case othertimes it's camel_case!

  • Most of the apis behave like apis. However, the Charging Sessions API is a for-purpose UX api. (/eadrax-chs/v2/charging-sessions). The startDate is pre-formated to some version of US date/time rather than ISO8601 (sometimes it says 'yesterday' other times it will have a mm/dd/yyyy formatted date)

  • the timelineItems array is a litteral json version of html with styling:

    {
        "body": {"actionType": "CHARGING_TYPE_UNSPECIFIED", "isEnabled": true, "subtitle": "Saturday 16:36", "title": "Plugged in"},
        "hasDivider": false,
        "leading": {
            "alignment": "CENTER", "backgroundColor": "TRANSPARENT", "backgroundOpacity": 1, "borderColor": "CONTENT_ACCENT", "borderOpacity": 1,
            "bottomLineColor": "PRIMARY", "bottomLineOpacity": 1, "color": "PRIMARY", "iconCode": 59692, "opacity": 1, "topLineColor": "TRANSPARENT",
            "topLineOpacity": 1
        },
        "trailing": {"actionType": "CHARGING_TYPE_UNSPECIFIED", "iconCode": 59694, "text": "57%"}
    },
  • dns for cocoapi.bmwgroup.us is unreliable and sometimes times-out. We need error capture and retry logic to manage this

bmw's People

Contributors

colinbendell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

mxfive thclaude

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.