Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 99 additions & 3 deletions docs/roborock_app.md
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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)
Expand Down
38 changes: 38 additions & 0 deletions patcher/patch_librrcodec.py
Original file line number Diff line number Diff line change
@@ -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("<I", 0xD503201F)
OFFSETS = (0x4ADCC, 0x4B428)


def main() -> 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())