Reverse Engineering the Official App Store

afourney edited this page Dec 17, 2016 · 13 revisions

NOTE: The URIs mentioned below are assigned by a URI bootsrapping process, and are user configurable (by changing one entry in a resource file), making a 3rd party app store a real possibility.

Notable endpoints for the store

"appstore/watchapps": "https://apps.getpebble.com/en_US/watchapps?platform=android&release_id=4&app_version=4.3&pebble_color=$$pebble_color$$&hardware=$$hardware$$&uid=$$user_id$$&mid=$$phone_id$$&pid=$$pebble_id$$&$$extras$$",
"appstore/watchfaces": "https://apps.getpebble.com/en_US/watchfaces?platform=android&release_id=4&app_version=4.3&pebble_color=$$pebble_color$$&hardware=$$hardware$$&uid=$$user_id$$&mid=$$phone_id$$&pid=$$pebble_id$$&$$extras$$",
"appstore/developer_apps": "https://apps.getpebble.com/en_US/developer/$$id$$?platform=android&release_id=4&app_version=4.3&pebble_color=$$pebble_color$$&hardware=$$hardware$$&uid=$$user_id$$&mid=$$phone_id$$&pid=$$pebble_id$$&$$extras$$",
"appstore/application": "https://apps.getpebble.com/en_US/application/$$id$$?platform=android&release_id=4&app_version=4.3&pebble_color=$$pebble_color$$&hardware=$$hardware$$&uid=$$user_id$$&mid=$$phone_id$$&pid=$$pebble_id$$&$$extras$$",
"appstore/application_changelog": "https://apps.getpebble.com/en_US/changelog/$$id$$?platform=android&release_id=4&app_version=4.3&pebble_color=$$pebble_color$$&hardware=$$hardware$$&uid=$$user_id$$&mid=$$phone_id$$&pid=$$pebble_id$$&$$extras$$",
"appstore/application_share": "https://apps.getpebble.com/applications/$$id$$",
"appstore/search": "https://apps.getpebble.com/en_US/search?platform=android&release_id=4&app_version=4.3&pebble_color=$$pebble_color$$&hardware=$$hardware$$&uid=$$user_id$$&mid=$$phone_id$$&pid=$$pebble_id$$&$$extras$$",
"appstore/search/query": "https://apps.getpebble.com/en_US/search/$$search_type$$?native=true&query=$$query$$&platform=android&release_id=4&app_version=4.3&pebble_color=$$pebble_color$$&hardware=$$hardware$$&uid=$$user_id$$&mid=$$phone_id$$&pid=$$pebble_id$$&$$extras$$",

Example Watchfaces Request

Here is an example of a request for the watchfaces storefront. Note that it is heavily cached by the mobile app. You likely won't see it in a traffic sniff unless you reinstall the Pebble app:

https://apps.getpebble.com/en_US/watchfaces?platform=ios&release_id=207&app_version=4.3.1&pebble_color=11&hardware=basalt&jsv=28&uid=REDACTED&pid=REDACTED&$$extras$$

This request is for a white Pebble Time.

The color is as defined in `pebble.h' in the C SDK. The color changes the app preview when viewing individual watchfaces. Note that the preview watch is always white, but the device type is determined from the color id.

The hardware are the usual suspects:

  • aplite
  • basalt
  • chalk
  • diorite
  • emery

I'm not sure what jsv is (javascript version?)

uid is the user identifier

pid is the Pebble serial number (printed on the back of the device)

HTML and Javascript

The Official store appears to be an Angular single-page application.

The store's de-minimized HTML code is here.

It contains a number of views:

  • views/application-changelog.html
  • views/application.html
  • views/category.html
  • views/collection.html
  • views/developer.html
  • views/directives/pbl-add-btn.html
  • views/directives/pbl-app-li.html
  • views/directives/pbl-app-slider.html
  • views/directives/pbl-banner-ad.html
  • views/directives/pbl-browser-header.html
  • views/directives/pbl-face-li.html
  • views/directives/pbl-face-slider.html
  • views/directives/pbl-heart.html
  • views/directives/pbl-pagination.html
  • views/directives/pbl-search-box.html
  • views/onboarding/getsomeapps.html
  • views/onboarding/migrate.html
  • views/partials/heart-icon.html
  • views/partials/logo.html
  • views/search.html
  • views/watchapps.html
  • views/watchfaces.html
  • placeholders/application.html
  • placeholders/category.html
  • placeholders/collection.html
  • placeholders/watchapps.html
  • placeholders/watchfaces.html

Protocol URIs

The HTML loaded within the mobile app can also calls various protocol URIs to get the native application to take certain actions. For example, loading the following URI in an iframe will result in a change to the title bar in the native app:

pebble-method-call-js-frame://?method=setNavBarTitle&args=%7B"methodName"%3A"setNavBarTitle"%2C"callbackId"%3A-1%2C"data"%3A%7B"title"%3A"Watchfaces"%7D%7D

The args is just a URI encoded JSON object:

{
    "methodName": "setNavBarTitle",
    "callbackId": -1,
    "data": {
            "title": "Watchfaces"
    }
}

These calls are managed by the Native class in the above mentioned app store HTML. E.g.,

Native.send("setNavBarTitle", { title: "Watchfaces" });

Native.send("openURL", { url: "http://www.rebble.io" });

This class works by packing the method name and args into a JSON object, encoding it as a URI, then opening the URI in a 1x1 iFrame:

function execute(uri) {
    var iframe = document.createElement("iframe");
    iframe.setAttribute("src", uri);
    iframe.setAttribute("height", "1px");
    iframe.setAttribute("width", "1px");
    document.documentElement.appendChild(iframe);
    iframe.parentNode.removeChild(iframe);
    iframe = null;
}

A list of the various native methods is as follows:

  • setNavBarTitle
  • openURL (used to open pages in the external browser)
  • loadAppToDeviceAndLocker (used to install apps!)
  • refreshAccessTokens
  • setVisibleApp
  • skipStep
  • bulkLoadAndClose

Installing an Application

So far, I've found 2 ways to install an application from within the app store web view:

  • Open a link the to pbw in a native browser via openURL
  • Call loadAppToDeviceAndLocker

The first is trivial to do, but requires more steps. The latter results in the one-click install we are all familiar with, but requires one to pack all the app metadata into the call (images, descriptions, links, etc).