KARR Key is an Android research app that demonstrates a security problem in some older, dealer-installed KARR vehicle security systems.
These devices are connected to the CAN bus, the internal network used by many systems in a vehicle. They need that access to lock doors, sound alarms, read vehicle information, and perform other security functions. Some older models also identify themselves over Bluetooth and use security information that was built into the original mobile apps. If that older connection and login process is reproduced, it can expose much of the vehicle control available to the KARR unit.
KARR Key makes that problem visible with a local, Bluetooth-based proof of concept. It is intended only for authorized research and testing. The app does not include recovered passwords or universal device keys.
These real-device screenshots were captured from KARR Key 1.0.3 on a Pixel 6a. Select an image to view it at full resolution.
- Find nearby KARR units by their recognizable Bluetooth names and wireless service.
- Connect to an authorized unit using research access information supplied by the user.
- Read information from the unit and send recovered vehicle or service commands.
- Show 25 unique operations without repeating the same command for different Customer, Dealer, or Installer workflows.
- Search the operations list or filter it with Reads, Commands, Unit mode, or Experimental. Deselect the active tag to show all operations; redundant workflow tags are omitted.
- Identify the connected model and operating mode, then label restrictions as either Hardware or Unit mode. Hardware-incompatible operations remain disabled; mode-restricted operations can be opened to see and start an authorized recovered mode-switch path.
- Show the difference between a command reaching Bluetooth, the KARR unit replying, and the vehicle physically doing something.
Vehicle operations, Bluetooth authentication, access keys, and history work locally with no account system or app backend. The Map page uses Android foreground location and requests OpenStreetMap tiles on demand, caching only tiles the user views. After a VIN is read, an the app automatically sends it to the public NHTSA vPIC service for decoding. If that lookup fails, the VIN remains visible with a manual retry action. The resulting vehicle identity is cached locally with encrypted storage.
Home briefly explains the security issue, shows the current connection status, and links to the research and firmware update instructions.
Device is where the user adds their research access information, scans for a unit, connects, authenticates, and views Bluetooth diagnostics. Saved access information is protected by Android Keystore and is never built into the app.
The Android scan is not restricted by a name or service filter. Every delivered BLE advertisement is classified using the recovered DR/QT formats, Dealer manufacturer frames (including embedded 13-character serials and QT6, QT7, DR1, DR2, CIU, and VW-programmer status types), branded names, and the shared transparent-UART service. Status-only manufacturer frames must match both the recovered model-bit layout and numeric five-character VIN-tail encoding; model bits alone are not unique enough. Possible matches are shown separately instead of being discarded, but only connectable confirmed-format results can be opened. Here, confirmed means the identifier and parser are present in recovered KARR source material; it does not by itself mean that model was physically tested. The app does not treat a MAC/OUI prefix as KARR proof: BLE addresses may be random, and a public prefix identifies the radio/interface vendor rather than the product or its owner.
End-to-end operation has been confirmed on real hardware advertising as DR
and QT units. Recognition of the other recovered families and formats is
source- and test-backed, but is not yet physical-hardware confirmation for
those models.
The app requests Android's maximum-duty BLE scan mode until the user stops it. This increases the chance of receiving a weak advertisement but does not raise the KARR unit's transmit power or the phone's receiver sensitivity. The Device screen stays awake during the scan because Android can pause an unfiltered scan after the screen turns off; the foreground notification provides a Stop action. If a connection unexpectedly drops, the app only tries to reconnect to the last unit that successfully authenticated.
Operations contains the searchable, filterable list of reads and commands. Operations that are incompatible with the connected KARR hardware are visibly disabled with a model-specific reason. An operation blocked only by the current unit mode remains selectable and explains the required mode. When the recovered transition graph and current access permit it, the detail page links to the appropriate mode switch or first step of a multi-step path.
One Installer immobilizer-output command is marked Experimental. It is sent only once, requires the ignition to be off, and does not treat a reply from the unit as proof that the vehicle was physically immobilized. Its detail page pairs the candidate with the separate recovered Unlock and disarm operation, presented there under the Restore drivable state safety heading. The candidate stays unavailable unless that recovery operation is authorized and compatible, and the user is instructed to verify a drivable state before testing.
The recovered Installer section also exposes switches to Installer, Dealer,
User, No sale, and Borrowed Car Agreement (BCA) modes. All five transitions have
been confirmed by READ_MODE on authorized hardware and are no longer
experimental. They are offered only from the source modes used by preserved
callers. Each CHANGE_MODE packet is sent once, then the app issues READ_MODE;
success is shown only when the unit reports the requested mode. Bootload and
Unconfigured are not exposed as ordinary mode switches because the preserved
apps do not use them that way. The Device Operations list keeps its compact
transition summaries. After a mode operation is selected, its command page shows
a Mode effect section explaining which security or service behavior the target
mode enables, disables, or temporarily changes.
Map plots KARR candidate advertisements from active scans using the phone's foreground location fix and each advertisement's RSSI. Stronger RSSI produces a smaller confidence circle; weaker RSSI and poorer location accuracy produce a larger circle. Map uses osmdroid with OpenStreetMap raster tiles and visible OSM attribution, and supports the full OSM zoom range. It does not use Google Maps or Google Play Services.
A Search button finds a previously discovered device by name, showing when it was last seen and jumping the map to it when selected. A Recenter button returns the map to the phone's current location without changing the zoom level. Map history is stored locally until the user clears it; device identifiers and coordinates are encrypted with Android Keystore-backed storage.
Settings has three tabs. Access keys manages the Customer and Dealer/Installer research access information used to authenticate with a unit, each independently clearable. Map shows how many map observations are stored, clears that history, and imports or exports it as a WiGLE-compatible CSV; importing a foreign WiGLE file only keeps devices that match KARR's discovery logic. Diagnostics runs a self-test and previews the most recent decoded Bluetooth notification.
- Finding a device over Bluetooth does not prove ownership or permission to use it.
- A Bluetooth connection is not enough to run commands. The KARR unit must complete its own authentication for the current connection.
- KARR Key supports the older authentication method recovered from the legacy apps. It does not support the newer per-unit method.
- KARR Key is not a firmware installer or a general-purpose CAN bus tool. It only exposes operations supported by the preserved app evidence.
- A response from the KARR unit does not prove that a door, alarm, trunk, or other physical part of the vehicle changed state.
- The app has been installed and smoke-tested on a Pixel 6a. That confirms the Android app and interface work; testing real vehicle behavior still requires an authorized compatible unit and direct observation.
The interface uses a greyscale light and dark theme. Home, Device, Operations, Map, and Settings are selected from the navigation bar, in that order. Operation details use the normal Android back action.
-
JDK 17. The Gradle build pins its compilation toolchain to JDK 17, so a JDK 17 must be installed and discoverable. Any distribution works (CI uses Temurin). A newer JDK (21, or Android Studio's bundled JBR) may also be on your
PATH— the toolchain still selects 17 — but pointJAVA_HOMEat the JDK 17 so Gradle itself launches on it. Confirm with:.\gradlew.bat -version
The
Launcher JVM:line should read17.x. If no JDK 17 is installed the build fails immediately with a clear "No compatible toolchains" error instead of silently compiling on the wrong JDK. -
Android SDK with platform
android-36,build-tools;36.0.0, andplatform-tools. Point Gradle at it withsdk.dirinlocal.propertiesor theANDROID_HOMEenvironment variable (either is sufficient). Without Android Studio, install the command-line tools and run:sdkmanager "platforms;android-36" "build-tools;36.0.0" "platform-tools"
A clone is source-only, so nothing special is required:
git clone <repo-url>The Gradle build lives in src/, not the repository root: the root holds no
settings.gradle.kts, so Gradle will not find the build from there. Run every
Gradle command shown below from src/, and open src/ (not the repository
root) in Android Studio.
cd srcThe original APKs and IDA databases are not build inputs and no longer live here; they are kept in a separate private evidence repository. See research/README.md for its location and how to request access.
For the normal edit-build-run loop, keep the Gradle daemon warm — this is the
default, so do not pass --no-daemon here — and build only what you need:
.\gradlew.bat :android:app:assembleDebug.\gradlew.bat installDebuginstallDebug builds and installs to a connected device or emulator. The debug
APK is written to src/android/app/build/outputs/apk/debug/KARR-Key.apk, uses
package name
com.karr.key, and is signed with the local debug keystore, so installDebug
(or adb install -r) updates it in place without wiping the stored access keys.
The app version lives in versionName/versionCode in
src/android/app/build.gradle.kts.
The Compose Desktop host in desktop/app reuses the same shared/* domain,
protocol, transport, and persistence modules as the Android app and
re-implements the UI for mouse, scroll-wheel, and keyboard. It adds an in-app
Terminal dock that shows the live BLE / protocol / authentication trace that
on Android only reaches adb logcat.
Run it, or build a self-contained installer or app image (all from src/):
.\gradlew.bat :desktop:app:run.\gradlew.bat :desktop:app:packageMsirun launches the window directly. packageMsi produces a double-click
installer under
src/desktop/app/build/compose/binaries/main/msi/ (needs the WiX Toolset);
packageDistributionForCurrentOS or createDistributable produce a runnable
app image (with a bundled JRE) that needs no installer and no WiX.
For real Bluetooth on Windows, build the native helper in
src/desktop/blebridge and set KARR_BLE=windows (see
that folder's README). Without a helper the app runs but scans nothing; set
KARR_BLE=mock to explore every screen and the terminal against a simulated
KARR unit with no hardware.
This mirrors what CI runs, plus the desktop and new shared modules; use
--no-daemon for a clean, reproducible result. Append
:android:app:assembleDebugAndroidTest if you also need the instrumented-test
APK:
.\gradlew.bat --no-daemon :shared:core:test :shared:protocol:test :shared:transport:test :shared:persistence:test testDebugUnitTest lintDebug assembleDebug :desktop:app:testshared/* are Kotlin/JVM modules, so their tests run under test rather than
the Android-only testDebugUnitTest.
On Linux or macOS, use ./gradlew.
The build also checks that Map's Internet and network-state permissions are
explicit, Bluetooth scan keeps neverForLocation, and foreground location
permissions are explicit. GitHub Actions runs this same tests/lint/debug build
on every push and pull request that can affect the APK; commits touching only
docs/, research/, or Markdown skip it. A scheduled prerelease runs weekly
and only when the main branch has changed in a way that affects the build,
reusing the APK CI already produced for that commit rather than rebuilding it.
Stable releases are produced entirely by GitHub Actions. Pushing a tag in the
form vMAJOR.MINOR.PATCH runs the tests and lint against that exact tagged
commit, builds and verifies a CI-signed release APK, and creates the GitHub
release with that APK as its only uploaded asset. GitHub displays the APK's
SHA-256 digest directly on the release page, while the release notes retain the
source commit, workflow run, version, and signing-certificate fingerprint. The
workflow rejects a tag whose version does not match the app's versionName;
release APKs are never uploaded manually.
For implementation details and exact safety rules, see DEVELOPMENT.md. For the preserved research evidence, see research/README.md.
KARR Key is not an official KARR, Webhouse Group, Southwest Dealer Services, or vehicle-manufacturer project. Product names are used only to identify the subject of this research.
No open-source license has been selected. Until one is added, this repository does not grant redistribution or modification rights. The original APKs retain their owners' rights.