Skip to content

Instantly share code, notes, and snippets.

@DavidBuchanan314
Last active July 9, 2024 16:49
Show Gist options
  • Save DavidBuchanan314/b071223bdae43ec309d996f176992d7c to your computer and use it in GitHub Desktop.
Save DavidBuchanan314/b071223bdae43ec309d996f176992d7c to your computer and use it in GitHub Desktop.
Unofficial RabbitLauncher.apk changelog

Unofficial Rabbit R1 APK Changelog

Executive Summary

The APK is stored in the firmware system partition, and gets updated as a side-effect of OTA firmware updates. Thus, a certain OS version implies a particular APK version. The OS version numbers are more compact, so I'll use them to identify APK versions below.

v0.8.50 seems like a pre-prod version that accidentally got shipped on some early devices. Like all future versions, it sends the device's IMEI during account activation.

v0.8.67 is the "launch day" firmware. It sets the OS-Version and App-Version HTTP headers. It also sends the device's IMEI during authentication.

I don't have information for versions v0.8.77, v0.8.78.

v0.8.83 appends a hardcoded string to the OS-Version HTTP header. This is the last release with no obfuscation whatsoever.

v0.8.86 introduces light-to-moderate obfuscation, and also takes steps to verify that it's running on a real R1 device. The actual behaviour of the app (in terms of how it talks to the API) is unchanged (except the hardcoded OS-Version suffix has a new value).

v0.8.99 introduces heavier obfuscation, documented here. It adds some new app integrity checks, along with a new Device-Health header, documented functionally here.

v0.8.103 makes no substantial changes.


v0.8.50

Build.DISPLAY: rabbit_OS_v0.8.50_20240407162326

APK versionName: 0.8.0

APK versionBrief: VerInfo: 0.8.0/8000/mainEnv, BuildTime: 2024-04-06 20:41:33/GMT-08:00(by yongle), Branch: release_sprint1/49cf6491(has uncommitted change: true),

This version apparently shipped in "Batch 0" (early batch 1?) devices.

It uses API endpoint wss://q1-main.transactional.pub/session, and prepends the string 0c8fef49-3dd7-4a3f-a199-f26e3452f6ed+ to the user's login token. It doesn't set any extra HTTP headers.

During account activation, the query parameter deviceId is appended to the URL, with value set to the device's IMEI.

v0.8.67

Build.DISPLAY: rabbit_OS_v0.8.67_20240420094131

APK versionName: 20240418.0-5-g4fd1bbff-dirty

APK versionBrief: VerInfo: 20240418.0-5-g4fd1bbff-dirty/2077777700/productionEnv, BuildTime: 2024-04-19 17:39:07/GMT-08:00(by jenkins), Branch: /4fd1bbff(has uncommitted change: true),

This is the version that most (?) Batch 1 devices shipped with, at launch. The APK was published on Twitter some time after.

The API endpoint moved to wss://r1-api.rabbit.tech/session, the login token prefix switched to rabbit-account-key+, and it adds the App-Version and OS-Version HTTP headers. The API authentication message now also includes the device's IMEI.

v0.8.77

Build.DISPLAY: rabbit_OS_v0.8.77_20240430100257

TODO: I haven't yet seen this version.

v0.8.78

Build.DISPLAY: rabbit_OS_v0.8.78_20240502195250

TODO: I haven't yet seen this version.

v0.8.83

Build.DISPLAY: rabbit_OS_v0.8.83_20240509120550

APK versionName: 20240429.6-1-g66aa0f20

APK versionBrief: VerInfo: 20240429.6-1-g66aa0f20/2077777700/productionEnv, BuildTime: 2024-05-08 20:03:39/GMT-08:00(by jenkins), Branch: /66aa0f20(has uncommitted change: false),

A static string is appended to the OS-Version HTTP header, which is returned by the getKey native method (stored in libbase.so).

v0.8.86

Build.DISPLAY: rabbit_OS_v0.8.86_20240523151103

APK versionName: 20240517.18-dirty

APK versionBrief: VerInfo: 20240517.18-dirty/2077777700/productionEnv, BuildTime: 2024-05-22 23:08:36/GMT-08:00(by jenkins), Branch: /40f4615a(has uncommitted change: (true),

This version introduces light obfuscation (symbol shortening) to the Java/Kotlin code.

The getKey native method is now moderately obfuscated (control flow flattening, string "encryption"), and attempts to verify that it's running on a real R1 device, before returning a similar hardcoded static string (different to the one in the previous version).

It checks for the presence of /sys/devices/platform/step_motor_ms35774/orientation (part of the R1's GPL-violating stepper motor driver, used for camera rotation).

It also checks the contents of /proc/cpuinfo, looking for Hardware\t: MT6765.

It also checks the android system property ro.product.model, looking for value r1

v0.8.99

Build.DISPLAY: rabbit_OS_v0.8.99_20240606175556

APK versionName: 20240603.15-dirty

APK versionBrief: VerInfo: 20240603.15-dirty/2077777700/productionEnv, BuildTime: 2024-06-06 01:50:56/GMT-08:00(by jenkins), Branch: /8d8ca2f4(has uncommitted change: true),

This version introduces heavier obfuscation, which I'm documenting separately, here. The main change is that many Java methods have been converted into obfuscated native methods. Some of the assets seem to be encrypted, and I haven't figured out how that works yet. I'll likely need an R1 in-hand to make progress on that front.

A new HTTP header was added, Device-Health, which I document the functionality of in more detail in my API Docs.

Implementation-wise, the Device-Health header value is computed using new native methods added to libbase.so. getKey no longer attempts to assess device integrity, instead returning a new hardcoded string unconditionally. The integrity checks were moved into a new function, getHealth, which performs the aforementioned checks, and some new checks, before returning a timestamp string. This timestamp string gets encrypted by another native method, loadPublicKeyAndEncrypt, which uses OpenSSL (staticly linked) to RSA-encrypt the timestamp string, using a hardcoded RSA public key.

The new checks are:

  • Checks that the APK was signed by one of two hardcoded signing keys (one is used in prod, and presumably the second is used during dev/testing)

  • Checks that the APK's package name is tech.rabbit.r1launcher.r1

v0.8.103

Build.DISPLAY: rabbit_OS_v0.8.103_20240620101341

APK versionName: 20240615.10-dirty

APK versionBrief: VerInfo: 20240615.10-dirty/2077777700/productionEnv, BuildTime: 2024-06-19 18:08:45/GMT-08:00(by jenkins), Branch: /039d6627(has uncommitted change: true),

There's a new OS-Version suffix, but the overall behaviour of the app is otherwise unchanged, in terms of how it communicates with the API.

v0.8.107

Build.DISPLAY: rabbit_OS_v0.8.107_20240701130253

APK versionName: 20240626.7-dirty

APK versionBrief: VerInfo: 20240626.7-dirty/2077777700/productionEnv, BuildTime: 2024-06-30 20:58:25/GMT-08:00(by jenkins), Branch: /b9373d4b(has uncommitted change: true),

There's a new library dependency, libsqlcipher.so (https://github.com/sqlcipher/sqlcipher). It's not obfuscated at all.

libbase.so has a new RSA public key: (actually this is the same key, just encoded in a slightly different format. Oops!)

-----BEGIN RSA PUBLIC KEY-----
MIIBigKCAYEAqLNRPcujKw1elkNJc+10o37YVbb7OjYa4Cv2pG2BzfSV3Ev7LMva
A2w0PAy25DhQU2NI7RU2a51OvTz0DsXM69oakuN0oSrKa9Eit2GPnX89H702MXGX
iRDZWEufAx67AaxK9d80Bajh2Abn06Bwaz9Z4D8vMxUOGsYkVKMW0LrmnW4984XI
UqT3+lOiEijBamodU/mORTeuxc5cdan00fq8qTOYuGFuKlPJSI3EExFHP3ONHD6z
44+PxXmhw532uAiNnT74yKXBoVYU19b8AAWLiSKyjf1eeus7dTobPKcpMemlJgxH
tVHtaSgnUugQ0a3XvmTVQpSeytPw8bL+/3c5KXfjGxPchoEZi7d71wv/AufDiSXr
gaew1KfJZBsr8Somr03b8xsHRJruPT61iPceh9bTWscwnK3WmDpAxnjdPQiflt/m
KkPEETtKGx0X5kUImHnr1jhUdYKmEOHfwkXBKVc66hpn85WGJ7MPVyixIOpzScAY
nKjVsP4ma6iFAgMBAAE=
-----END RSA PUBLIC KEY-----
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment