Files
dwarf_protocol/00_methodology.md
Jacquin Antoine be87e8a4ca Add complete DWARFLAB protocol documentation
- Architecture overview, BLE and WebSocket protocols
- All 19 modules with command IDs validated against smali source
- BLE packet format with CRC-16/MODBUS pseudocode
- Complete WsCmd index (~300 commands)
- Connection flow with pseudocode examples
- Methodology section explaining APK decompilation process
2026-06-13 00:21:19 +02:00

5.6 KiB

Methodology & Sources

How This Documentation Was Obtained

All information in this documentation was extracted through static analysis of the decompiled DWARFLAB Android APK (v3.4.0, build 629, package com.convergence.dwarflab). No device was needed — the entire protocol is defined in the app's code.

Decompilation

The APK was decompiled using apktool v3.4.0, which produced:

  • AndroidManifest.xml — permissions, activities, services
  • smali/ through smali_classes7/ — 7 DEX files of dalvik bytecode
  • lib/ — native .so libraries (arm64, armeabi-v7a, x86_64)
  • assets/ — voice JSON, country JSON, keystores
  • res/ — layouts, drawables, Lottie animations

Key Smali Files Mapped to Protocol Concepts

Protocol Concept Smali File Location
BLE UUIDs cg1.smali smali_classes4/
BLE commands j80.smali smali_classes4/
BLE packet format l80.smali smali_classes4/
BLE CRC-16 vc0.smali smali_classes4/
WebSocket URL v55.smali smali_classes4/
WsPacket fields BaseProto$WsPacket.smali smali_classes5/
WsModuleId WsModuleId.smali smali_classes4/
WsCmd (all commands) WsCmd.smali smali_classes4/
WsMessageType WsMessageType.smali smali_classes4/
WsRespCode WsRespCode.smali smali_classes4/
DeviceType DeviceType.smali smali_classes4/
BluetoothPacketSender BluetoothPacketSender.smali smali_classes4/
ConnectionManager ConnectionManager.smali smali_classes4/
Proto messages AstroProto*.smali, CameraProto*.smali, etc. smali_classes5/

Extraction Methods

1. UUID Discovery (cg1.smali)

The cg1 class contains 8 static UUID strings. Read directly from the <clinit> method — the values are string constants embedded in the bytecode.

2. Command IDs (WsCmd.smali)

The WsCmd enum contains ~300 entries. Each entry is initialized in <clinit> via <init>(String name, int ordinal, int cmd). The third parameter (cmd) is the numeric command ID. Extracted by tracing register values through the constructor calls.

Important: Kotlin enum ordinals and explicit cmd IDs are different numbers. The cmd value is what goes on the wire.

3. Module IDs (WsModuleId.smali)

The WsModuleId enum uses ordinal values only (no custom id field). Module ID = ordinal position in the enum (0-18).

4. BLE Packet Format (l80.smali)

The l80 class (obfuscated name for BluetoothPacket) encodes packets in method j():

  • Magic byte 0xAA at position 0
  • 9-byte header: protocolVersion, cmdInstruction, packageSequence, totalPackages, extendedProtocol (2B), validDataLength (2B)
  • Payload: protobuf data
  • CRC-16 computed by class vc0 over header+payload
  • End marker 0x0D

The CRC lookup table in vc0.smali starts with {0x0000, 0xC0C1, 0xC181, 0x0140, ...} which identifies it as CRC-16/MODBUS (reflected polynomial 0xA001 = 0x8005 reflected, init 0xFFFF).

5. WebSocket URL (v55.smali)

The URL is assembled by concatenating: "ws://" + yd3.b() (host IP) + ":9900/?client_id=" + y32.c() (client ID). Two additional constants: 0x1139 (4409) and 0x113A (4410) for secondary ports.

6. DeviceType IDs (DeviceType.smali)

The constructor signature is <init>(String name, int ordinal, int deviceId, int nameResId). The deviceId parameter is the on-wire device identifier. Careful register tracing is required because smali reuses registers:

  • Unknown: deviceId=0
  • DWARF_2: deviceId=1
  • DWARF_3: deviceId=2
  • DWARF_mini: deviceId=4 (ordinal=3, gap at deviceId=3)
  • DWARF_4: deviceId=5
  • DWARF_DRAGON: deviceId=6

7. Proto Message Fields (smali_classes5/com/convergence/dwarflab/proto/)

Each .smali file in the proto package corresponds to a protobuf message. Field numbers are embedded as constants in the writeTo() method (serialized using writeString(fieldNumber, value), writeInt32(fieldNumber, value), etc.). Field names are preserved in the obfuscated class names via Kotlin metadata annotations.

Limitations

  • Obfuscated names: Most class names are obfuscated (e.g., l80, cg1, j80). Kotlin metadata annotations help recover some names.
  • No .proto files: The original .proto source files are not included in the APK. Field names and numbers are inferred from the generated Java/Kotlin code.
  • String literals: Some strings (UUIDs, URLs, keys) are embedded directly and can be extracted with certainty. Others require contextual inference.
  • Behavioral details: Timing, retry logic, and UI flow are inferred from the code but not directly observable without a device.
  • Version-specific: This documentation corresponds to APK v3.4.0. Newer or older versions may differ.

Verification

All data points were cross-validated in two passes:

  1. First pass: Initial extraction from smali source code
  2. Second pass: Independent re-reading of each smali file to confirm register values, field numbers, and constants

Key verifications:

  • All 8 BLE UUIDs confirmed from cg1.smali
  • All 19 module IDs confirmed from WsModuleId.smali (ordinals)
  • All ~300 WsCmd command IDs confirmed from WsCmd.smali (hex → decimal conversions)
  • All 8 WsPacket field numbers confirmed from BaseProto$WsPacket.smali
  • DeviceType IDs confirmed with register tracing (including the gap at deviceId=3)
  • BLE command bytes (0-7) confirmed from j80.smali
  • BLE packet format confirmed from l80.smali (header layout, CRC, end marker)
  • WebSocket URL and ports confirmed from v55.smali
  • CRC-16/MODBUS confirmed from vc0.smali lookup table