diff --git a/docs/roborock_app.md b/docs/roborock_app.md index 49c82bb..174eb25 100644 --- a/docs/roborock_app.md +++ b/docs/roborock_app.md @@ -1,9 +1,9 @@ # Using the Roborock App -ONLY WORKS ON IOS FOR NOW! Android does certificate pinning that makes this harder to do. See info [here](https://github.com/Python-roborock/local_roborock_server/issues/5#issuecomment-4223935907) - Use this after [Installation](installation.md) and [Onboarding](onboarding.md) if you want the official Roborock app to talk to your local stack. +## iPhone + 1. Log out of the app on your phone. 2. On a machine that is not running the server, run the MITM script: @@ -14,10 +14,106 @@ Use this after [Installation](installation.md) and [Onboarding](onboarding.md) i 3. Install the WireGuard app on your phone. Then tap the plus button in WireGuard, choose to add from QR code, and scan the code at `http://127.0.0.1:8081/#/capture`. -4. Open `mitm.it` in your web browser. Follow the instructions there for your device. On iPhone, open it in Safari and complete all device-specific steps, including installing and trusting the certificate. +4. Open `mitm.it` in your web browser (iPhone). Follow the instructions there for your device. In Safari, complete all device-specific steps, including installing and trusting the certificate. 5. Once the MITM setup is working, open the Roborock app, log back in, enter your verification code, and the server should automatically show the vacuums already known to your local stack. Turn off WireGuard, disable the MITM certificate, and then open one of your devices to confirm the map loads. +## Android + +> **Note:** This workaround has only been tested and confirmed working on Roborock app version **4.60.06**. Newer versions may ship a different `librrcodec.so` with different offsets, in which case the patch script will need to be updated. + +Android 7+ (API level 24+) only trusts system certificates, so the app will reject the MITM certificate by default. On top of that, the Roborock app includes a native library (`librrcodec.so`) that checks the APK's signing certificate on startup and kills the process if it doesn't match, meaning a simple `apk-mitm` patch isn't enough. + +To work around both issues, you need to patch `librrcodec.so` to remove the integrity check, then repackage the APK. + +### Prerequisites + +Make sure you have the following installed: + +- [apk-mitm](https://github.com/nicbarker/apk-mitm) (`npm install -g apk-mitm`) +- [apktool](https://apktool.org/) +- [apksigner](https://developer.android.com/tools/apksigner) (part of Android SDK build-tools) +- [keytool](https://docs.oracle.com/en/java/javase/17/docs/specs/man/keytool.html) (part of JDK) +- [Python 3](https://www.python.org/downloads/) + +### Patching the APK + +1. Download the Roborock APK (e.g. from [APKMirror](https://www.apkmirror.com/apk/roborock/roborock/)) and place it in your working directory. + +2. Run `apk-mitm` to patch the APK for certificate trust: + + ```bash + apk-mitm roborock.apk + ``` + +3. Decompile the patched APK: + + ```bash + apktool d roborock-patched.apk -o roborock_work + ``` + +4. Patch `librrcodec.so` to remove the signature integrity check. + + First, save a copy of the original `librrcodec.so` as `librrcodec.so.bak`. + + ```bash + cp roborock_work/lib/arm64-v8a/librrcodec.so roborock_work/lib/arm64-v8a/librrcodec.so.bak + ``` + + Now run the patcher (located in .\patcher\). You can pass the path to `librrcodec.so` explicitly (as shown below), or run it with no arguments from inside the unpacked APK and it will find the file itself. + + > Use `python` or `python3` depending on your system (Python 3.8+ is required). + + ```bash + python patch_librrcodec.py roborock_work/lib/arm64-v8a/librrcodec.so + ``` + +5. Rebuild the APK: + + ```bash + apktool b roborock_work -o roborock_final.apk + ``` + +6. Sign the APK. You have two options: + + **Option A** — Create a new signing key and sign: + + ```bash + keytool -genkey -v -keystore my-key.keystore -alias mykey -keyalg RSA -keysize 2048 -validity 10000 + apksigner sign --ks my-key.keystore roborock_final.apk + ``` + + **Option B** — Use an existing JKS keystore: + + ```bash + apksigner sign --ks my-key.jks --v1-signing-enabled true --v2-signing-enabled true roborock_final.apk + ``` +7. Uninstall the original Roborock app from your phone (required because the signing key is different), then install the patched APK: + + ```bash + adb uninstall com.roborock.smart + adb install roborock_final.apk + ``` + +8. On a machine that is not running the server, run the MITM script: + + ```bash + uv run mitm_redirect.py --local-api api-roborock.example.com + ``` + +9. Install the WireGuard app on your phone. Then tap the plus button in WireGuard, choose to add from QR code, and scan the code at `http://127.0.0.1:8081/#/capture`. + +10. Open `mitm.it` in your web browser (Phone). Follow the instructions there for your device. In Chrome, complete all device-specific steps, including installing and trusting the certificate. + +11. Once the MITM setup is working, open the Roborock app, log back in, enter your verification code, and the server should automatically show the vacuums already known to your local stack. Close Roborock App, Turn off WireGuard, disable/delete the MITM certificate, and then open the Roborock App, Select your device/s to confirm the map loads. + + +### What the patch does + +During `JNI_OnLoad`, `librrcodec.so` calls a verification function that retrieves the APK's signing certificate via `PackageManager.getPackageInfo()`, hashes it with `MessageDigest`, and compares it against a hardcoded value. If the hash doesn't match, it calls `Process.killProcess()`. The patch replaces the two `BL` (branch-link) instructions that call this function (at VA `0x4bdcc` and `0x4c428`) with `NOP`, so the check never runs. The crypto functions the app actually needs are unaffected. + +> **Note:** This patch has only been tested and confirmed working on Roborock app version **4.60.06**, against the `librrcodec.so` with build ID `becc35bc1a75903df1eae3f90b380ca5403d06cb`. If Roborock releases a new app version, the offsets may change and the patch script will need to be updated. + ## Related Docs - [Installation](installation.md) diff --git a/patcher/patch_librrcodec.py b/patcher/patch_librrcodec.py new file mode 100644 index 0000000..0e7b3f3 --- /dev/null +++ b/patcher/patch_librrcodec.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Patch librrcodec.so to remove the signing-cert check. + +Tested on Roborock app 4.60.06. +""" + +import struct +import sys +from pathlib import Path + +NOP = struct.pack(" int: + if len(sys.argv) > 1: + target = Path(sys.argv[1]) + else: + found = list(Path.cwd().rglob("librrcodec.so")) + if not found: + print("usage: patch_librrcodec.py [path/to/librrcodec.so]") + return 1 + target = next((p for p in found if "arm64-v8a" in p.parts), found[0]) + print(f"Found: {target}") + + with open(target, "r+b") as f: + data = bytearray(f.read()) + for offset in OFFSETS: + data[offset:offset + 4] = NOP + f.seek(0) + f.write(data) + + print("Patched successfully") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) \ No newline at end of file