# Reviving a BlackBerry KEY2 (and Finding a Bug in the Unlock Tool)

Source: https://gofranz.com/blog/reviving-a-blackberry-key2-and-a-bug-in-the-unlock-tool/

I've had a BlackBerry KEY2 sitting in a drawer since it stopped getting updates. Physical keyboard, small screen, the kind of phone that ruins you for glass slabs - but the software underneath had been dead for years. This week I finally put LineageOS on it, with Claude Code doing most of the driving.

There's a small community keeping this device alive (the Lineage-BBKey2 project, unofficial, actively maintained), and the overall recipe is well documented: unlock the bootloader with a tool called `kibo` that exploits a fastboot buffer overflow, reflash BlackBerry's own signed firmware through their autoloader, then sideload LineageOS and GApps through recovery. None of that is new. What made it interesting is that I was doing it from a Guix machine, and Guix doesn't play along with anything that expects a normal Linux filesystem.

### Guix has no `/lib64/ld-linux-x86-64.so.2`

The obvious tools - Google's platform-tools, `kibo` - are prebuilt Linux binaries that expect `/lib64/ld-linux-x86-64.so.2` and a handful of libraries at fixed paths. None of that exists on Guix. Rather than reach for a container, `patchelf` fixes this directly:

```sh
$ patchelf --set-interpreter $GLIBC/lib/ld-linux-x86-64.so.2 \
           --set-rpath "$GLIBC/lib:$LIBUSB/lib" kibo
```

Point the interpreter and rpath at the right store paths, and the binary runs natively - no FHS emulation, no container, full USB access. Worth doing over Guix's own `adb`/`fastboot` package too, which is stuck at 7.1.2 from 2016; patching Google's current 37.0.1 build instead meant sparse image splitting actually worked on a 3.2GB `system.img` against a 512MB per-transfer limit.

### USBGuard doesn't know what a BlackBerry is

The phone showed up on USB, fully enumerated, and fastboot still couldn't see it. Turned out `authorized_default` on every root hub was `0`, and USBGuard's implicit policy is block-by-default; there was simply no rule for BlackBerry's vendor ID. Fine for a laptop that shouldn't trust random USB devices, less fine when the random USB device is the phone you're trying to flash.

The annoying part: the phone re-enumerates under a new device ID at every single stage - MTP, fastboot, bootloader, recovery, sideload - so a one-off `usbguard allow-device` only gets you through the next reboot. The fix was a small loop watching for newly-blocked devices from the relevant vendor IDs and auto-allowing them, entirely through USBGuard's IPC as a member of `wheel`, never touching the persisted policy file. Runtime-only, gone the moment you kill it.

### `kibo` said the device wasn't exploitable. It was lying.

This was the fun one. After flashing BlackBerry's firmware twice (their A/B implementation is broken enough that the community instructions just say "do it twice"), `kibo`'s device check kept reporting one of the two boot-chains as `INVALID` and refusing to unlock - on a phone that had, by every other measure, been flashed correctly.

Rather than trust that, we wrote a tiny raw USB probe with `pyusb` to dump exactly what the device sends back for its info query. And there it was: `kibo` reads fixed-size response buffers off the wire and never null-terminates them, so its `strcmp()` calls occasionally read past the real bytes into whatever was left on the stack. One buffer happened to land next to a stray zero; the other didn't. Deterministic, reproducible, and completely wrong about the device.

`kibo` is GPL and genuinely small - three source files. Pulled it, added three lines to null-terminate each response at its actual length, and rebuilt:

```sh
$ guix shell cmake make gcc-toolchain pkg-config libusb-next -- \
    cmake -B build && cmake --build build
```

Second run: `Your device is vulnerable.` Which, for once, was exactly what I wanted to hear.

### The result

Unlock went clean after that, LineageOS 22.2 and MindTheGapps sideloaded without a hitch, and the KEY2 booted into Android 15 on a Snapdragon 660 from 2018. The physical keyboard needed one more small fight - Gboard doesn't reliably suppress itself for a hardware keyboard on this build - solved by switching to K12KB, the keyboard app the ROM ships for exactly this reason.

I'm not going to pretend it's tidy. It's a 2018 phone running an unofficial port, dual SIM has a known bug I sidestepped by only using one, and Google Wallet is a coin flip I decided not to chase. But the keyboard clicks like it's new, and the phone that had been sitting in a drawer for two years is back in my pocket. Good luck :)
