# QR Code Analysis ## Scope The QR code system on Pokemon Pro involves **two separate code paths**: 1. **QR Code Generation** — The game uses Nayuki's `qrcodegen` library to generate QR codes (WiFi config, Stern Insider Connected login, support URLs) and display them on-screen. 2. **QR Code Scanning** — A standalone hardware scanner module connected to the game via a `node4` bridge board (LPC812/LPC1124 MCU on the SPIKE-3 nodebus) and a serial UART. **Important finding**: The QR code scanning functionality is in the **main game binary** (`/games/pokemon_pro/game`), **not** in `conagent`. The `conagent` binary has no QR-related strings or code. See `conagent-re.md` for the connectivity agent's separate role. ## Source Binary - Target: `/games/pokemon_pro/game` - Workspace path: `games/pokemon_pro/game` - SHA-256: (see `artifact-index.md`) - Type: stripped AArch64 PIE ELF, 52,829,400 bytes - Embedded library: `qrcodegen` (Nayuki's QR Code Generator, C library) ## QR Code Generation ### Library: qrcodegen The game embeds **Nayuki's QR Code Generator** library (`qrcodegen.c` at file offset `0x25e6c88`). This is a standalone C library that generates QR code symbol data from text input. Confirmed API functions: | Function | Offset | Purpose | |---|---|---| | `qrcodegen_encodeSegmentsAdvanced` | `0x26028f8` | Core encoding entry point | | `qrcodegen_getSize` | `0x2602760` | Get QR code module size | | `qrcodegen_getModule` | `0x2602998` | Get individual module bit | | `qrcodegen_isNumeric` | `0x2602728` | Check if text is numeric mode | | `qrcodegen_isAlphanumeric` | `0x26029b0` | Check if text is alphanumeric mode | | `qrcodegen_makeBytes` | `0x2602a00` | Create byte segment | | `qrcodegen_makeNumeric` | `0x2602a18` | Create numeric segment | | `qrcodegen_makeAlphanumeric` | `0x2602a38` | Create alphanumeric segment | | `qrcodegen_makeEci` | `0x2602a30` | Create ECI segment | Internal assertion strings confirm library version constraints: - `qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX` (0x25e6c98) - `21 <= qrsize && qrsize <= 177` (0x25e6d90) - `0 <= (int)ecl && (int)ecl < 4` (0x25e7070) ### QR Code Output: BMP Files Generated QR codes are rendered as **BMP image files**: | Path | Offset | Purpose | |---|---|---| | `/tmp/qrx_.bmp` | `0x25db068` | Temporary QR code image | | `/tmp/login.bmp` | `0x25e1760` | Login QR code output | | `/connectivity/qr_code_storage/%d.bmp` | `0x25e26e8` + `0x25e2718` | Persistent QR code storage | ### QR Code Configuration Properties QR code display properties are configured via a JSON-like object with these fields (all at offsets `0x25db270`–`0x25db2e0`): | Field | Type | Purpose | |---|---|---| | `qrCodeText` | string | The text/URL to encode in the QR code | | `qrCodeAppendMachineUuid` | bool | Whether to append machine UUID to text | | `qrCodeStartTime` | timestamp | QR code validity window start | | `qrCodeEndTime` | timestamp | QR code validity window end | | `qrCodeXPosition` | int | Screen X position | | `qrCodeYPosition` | int | Screen Y position | | `qrCodeSize` | int | QR code size in pixels | ### Generated QR Code Types | Type | String Context | Offset | |---|---|---| | Stern Insider Connected login | `Stern Insider Connected™ QR code.` | `0x25a7dba` | | Connected Login QR | `Connected™ Login QR code.` | `0x25a7e99` | | WiFi QR Code | `Scan WiFi QR Code` | `0x25a8360` | | Quick Registration | `Connectivity/QrCodes/quick_registration_qr_code` | `0x25e1cf8` | | Support | `Connectivity/QrCodes/support` | `0x25dc7e8` | | Tech Support | `SCAN THIS QR CODE TO REACH OUR TECH SUPPORT TEAM` | `0x25dc231` | ## QR Code Scanning — Hardware Architecture ### Physical Components The QR scanner system consists of: 1. **Scanner module** — A standalone 2D barcode scan engine (OEM part `part:520-9865-00` at `0x25b8c20`). The specific module model (Zebra, Honeywell, Newland, etc.) is not identifiable from strings; the firmware is a thin bridge without scanner-brand identifiers. 2. **Node4 bridge board** — A `node4` SPIKE-3 nodebus peripheral with dual firmware options: - `node4-LPC812-1_33_0.hex` (LPC812, Cortex-M0+, 12 KB decoded) - `node4-LPC1124_303-1_33_0.hex` (LPC1124, Cortex-M0+, 14 KB decoded) 3. **Connections**: - Ribbon cable from scanner module to node4 board - RJ45 nodebus cable from node4 to the game system 4. **GPIO antenna switch**: Two RF antennas on `gpiochip1` lines 3 (`ant1`) and 7 (`ant2`), controlled via `libgpiod.so.2` ### Serial Interface The game communicates with the scanner module via **serial UART**: | Path | Selection Logic | Offset | |---|---|---| | `/dev/ttyAMA2` | Default (kernel release byte != '5') | `0x25e9468` | | `/dev/ttyAMA5` | Kernel release byte == '5' | `0x25e9478` | Selection function at VA `0x94b8b0`: Uses `uname()` to check a kernel release byte, compares to `0x35` (`'5'`), selects ttyAMA5 on match, ttyAMA2 otherwise. ### Serial Configuration (VA `0x92d350`) - **Open**: `open(path, O_RDWR | O_NOCTTY)` - **Termios2**: Uses `TCGETS2` (ioctl `0x541e`) / `TCSETS2` (ioctl `0x541f`) — `struct termios2` for raw baud rates - **Data bits**: 8 data bits, no parity - **Stop bits**: 1 stop bit (configurable to 2 via `config[5] == 2`) - **Local mode**: `CLOCAL | CREAD` (`0x880`) - **Baud rate**: Configurable via 4-entry table indexed by `config[4] - 5`; actual rates not extractable from static analysis (uses non-standard termios2 CBAUD encodings) - **Flow control**: `tcflow(fd, TCOON)`, `tcflush(fd, TCIOFLUSH)` - **Timeout**: 1000 ms via `tcdrain` ### Node4 Bridge Firmware The `node4` firmware files are thin, simple firmware images (6-11 ARM Thumb function entries) that bridge between: - **Scanner side**: UART connection to the scanner module (baud rate divisors suggest 57600 for LPC812, 9600/230400/38400 for LPC1124) - **Nodebus side**: SPIKE-3 nodebus protocol with node address `0x04` The firmware does **not** contain: - Scanner decode libraries (zbar, quirc, etc.) - Scanner brand identifiers or protocol tables - Any human-readable strings at all This confirms the scanner module is a pre-integrated 2D barcode engine that handles decode internally, and the node4 board is a thin UART-to-nodebus bridge. ## QR Scanner Protocol ### Bridge Protocol (VA `0x94b9d0`–`0x94bbfc`) The game communicates with the bridge (and through it to the scanner) using a custom framed protocol: - **Buffer size**: 256 bytes (`mov w2, #0x100`) - **Frame format**: `STX` + escaped body (cmd, seq, payload, checksum) + `ETX` - **STX**: `0x02` — start of frame - **ETX**: `0x03` — end of frame, triggers message processing - **ESC**: `0x1B` — escape byte; next byte has bit `0x80` set - **Checksum**: `(-sum(body)) & 0xFF`, verified before dispatch - **Valid commands**: `0x10` through `0x41` (checked via `sub w0, w2, #0x10; cmp w0, #0x31`) - **Command 0x30**: Firmware CRC handler at VA `0x953fc0` - **Error handling**: "bridge rx checksum error", "message overrun", "too many message", "bridge %s unknown command: 0x%02x" ### Scanner Status Register (16 states) The game tracks scanner status via an enum/string table at `0x25b8a40`–`0x25b8c00`: | Offset | String | Meaning | |---|---|---| | `0x25b8a40` | `QR SCANNER BACKLIGHT` | Backlight control command | | `0x25b8a58` | `QR SCANNER STATUS READY` | Scanner is ready for a scan | | `0x25b8a70` | `QR SCANNER STATUS STRING` | Text string decoded from QR | | `0x25b8a90` | `QR SCANNER STATUS ENCODED DATA` | Raw binary encoded data received | | `0x25b8ab0` | `QR SCANNER STATUS MISSED STRING` | A scan was started but no decode | | `0x25b8ad0` | `QR SCANNER STATUS OVERRUN` | Buffer overrun condition | | `0x25b8af0` | `QR SCANNER STATUS UNSUPPORTED_AP` | Unsupported access point (WiFi) | | `0x25b8b18` | `QR SCANNER STATUS FIFO OVERRUN` | Hardware FIFO overrun | | `0x25b8b38` | `QR SCANNER STATUS CONFIG` | Configuration change event | | `0x25b8b58` | `QR SCANNER STATUS ERROR` | Generic error | | `0x25b8b70` | `QR SCANNER STATUS ERR1` | Error code 1 | | `0x25b8b88` | `QR SCANNER STATUS ERR2` | Error code 2 | | `0x25b8ba0` | `QR SCANNER STATUS ERR3` | Error code 3 | | `0x25b8bb8` | `QR SCANNER STATUS ERR4` | Error code 4 | | `0x25b8bd0` | `QR SCANNER STATUS ERR5` | Error code 5 | | `0x25b8be8` | `QR SCANNER STATUS ERR6` | Error code 6 | | `0x25b8c00` | `QR SCANNER STATUS NO READER` | Scanner disconnected | ### GPIO Antenna Switch (VA `0x950260`) The QR scanner has a **dual-antenna RF switch** controlled via `gpiochip1`: | GPIO | Label | Function | |---|---|---| | Line 3 | `ant1` | Antenna 1 | | Line 7 | `ant2` | Antenna 2 | Control logic: - `arg=0`: Both antennas off (low) - `arg=1`: `ant1` high, `ant2` low - `arg=2`: `ant2` high, `ant1` low ## QR Parsing Classes ### C++ Class Hierarchy The game uses C++ classes for QR code parsing, identified from RTTI/vtable references at `0x25fe1c0`–`0x25fe2d0`: | Class | Offset | Role | |---|---|---| | `QrOfflineParser` | `0x25fe1c0` | Parses offline/local QR codes | | `NetworkQrParser` | `0x25fe218` | Parses network/connectivity QR codes | | `QrOfflineListener` | `0x25fe2d0` | Listens for offline QR scan events | | `NetworkDataInterface` | `0x25fe1d8` | Base interface for network data | | `CommandInterface` | `0x25fe1f0` | Base interface for commands | | `NetworkData` | `0x25fe208` | Network data container | ### QR Code Format: `REG:T:` The game uses a **proprietary QR code data format** identified by the prefix `REG:T:` at `0x25dbe30`: - `REG:T:WPA` (offset `0x25dbe30`) — WiFi configuration QR code - `REG:T:ETH` (offset `0x25dbe40`) — Ethernet/machine registration QR code The format extends `REG:T:{TYPE}:{key}:{value}:{key}:{value}:...` with fields parsed programmatically. ### WiFi QR Data Flow When a `REG:T:WPA` QR code is scanned: 1. `NetworkQrParser` extracts SSID, PSK, and encryption type 2. Validation: `"Not a valid WiFi QR code"` (`0x25a7200`) 3. Game builds `wpa_supplicant` config block: ``` ssid="%s" scan_ssid=1 key_mgmt=%s (WPA-PSK) pairwise=%s (TKIP or "CCMP TKIP") group=%s psk=%s ``` 4. Writes to `/etc/wpa_supplicant.conf` (`0x25dfd68`) 5. Controls WiFi via `wpa_supplicant` control interface: - `ADD_NETWORK` (`0x25d5688`) - `SET_NETWORK` (`0x25d5808`) - `ENABLE_NETWORK %s` (`0x25d5780`) - `CTRL-EVENT-CONNECTED` (`0x25d57e8`) - `CTRL-EVENT-DISCONNECTED` (`0x25d57e8`) 6. Control socket: `/var/run/wpa_supplicant/wlan0` (`0x25d5708`) 7. Also uses `/usr/sbin/wpa_cli status` (`0x25e26b8`) ### Stern Insider Connected QR Data Flow When a Stern Insider Connected login QR code is scanned: 1. `QrOfflineParser` extracts user UUID from QR code 2. Flow: `QERT_SCANNED_USER_ACCOUNT` (`0x25dedd0`) → `ScannedUserAccountUUID` (`0x25dd568`) → `AuthenticatePlayer` (`0x25dd590`) 3. Player UUID is sent to backend via `PLAYER_AUTHENTICATE` (`0x25ecf18`) 4. Audit counters track results: - `AUD_CONNECTIVITY_USERS_SCANNED` (`0x2594d60`) - `AUD_CONNECTIVITY_USERS_JOINED` (`0x2594d80`) - `AUD_CONNECTIVITY_BAD_QR_SCANS` (`0x2594ea8`) - `AUD_CONNECTIVITY_BAD_USER_SCANS` (`0x2594e88`) ## Exploit Surface Analysis ### High Priority Targets #### 1. `REG:T:WPA` → wpa_supplicant Command Injection **Risk**: HIGH. The QR code content is parsed and passed to `wpa_supplicant` control interface commands (`ADD_NETWORK`, `SET_NETWORK`, `ENABLE_NETWORK`). If the SSID or PSK fields are not properly sanitized, an attacker could: - Inject arbitrary `wpa_cli` or `wpa_supplicant` control interface commands - Write to `/etc/wpa_supplicant.conf` with crafted content - Potentially execute shell commands via the control interface **Evidence**: The game uses `wpa_cli` at `/usr/sbin/wpa_cli status` (`0x25e26b8`). The control socket at `/var/run/wpa_supplicant/wlan0` (`0x25d5708`) accepts textual commands. SSID and PSK are inserted into format strings (`ssid="%s"`, `psk="%s"`) — if these use `sprintf`/`snprintf` without length limits, buffer overflow is possible. #### 2. Serial Bridge Buffer Handling **Risk**: MEDIUM-HIGH. The bridge protocol uses a **256-byte buffer** with a **1000 ms timeout**. The frame format uses STX/ETX/ESC framing with checksum. Potential issues: - Buffer overflow if a message larger than 256 bytes is sent (the `read()` call reads up to 256 bytes, but the parser accumulates across multiple reads) - Escape sequence mishandling (ESC `0x1B` sets bit 0x80 on next byte — crafted sequences could cause state confusion) - Checksum bypass (the checksum is a simple sum complement) #### 3. RapidJSON Parsing of QR Content **Risk**: MEDIUM. The game uses **RapidJSON** library to parse QR-related data. Failure messages: - `Failed to parse JSON.` (`0x259e9e0`) - `INVALID JSON: expected "achievement_descriptors"` (`0x25dff48`) - `INVALID JSON: expected "machine"` (`0x25e0088`) - `INVALID JSON: failed to parse "machine"` (`0x25e00b0`) RapidJSON assertion failures at `0x25d6348`–`0x25d6f20` (internal type checks). If assertions are compiled in (debug build), malformed JSON can crash the process. If assertions are compiled out, type confusion bugs may be exploitable. #### 4. `qrCodeText` Buffer Handling **Risk**: MEDIUM. The `qrCodeText` field is a raw string field that gets encoded into QR code data. If the text is used in `sprintf`/`snprintf` for display or file path construction without proper bounds checking, it could be a buffer overflow vector. QR codes are saved to `/connectivity/qr_code_storage/%d.bmp` using a numeric index — if the index is attacker-controlled, format string injection via `%d` is possible. #### 5. Nodebus Secure Protocol **Risk**: MEDIUM. The netbridge emulator (`netbridge_emu.py`) documents that secure nodebus requests (command `0x11`) use XOR encryption with session key rotation when `wire_address & 0xC0 == 0xC0`. If the encryption is weak or the session key derivation is predictable, a physical attacker with nodebus access could forge QR scan results. ### Medium Priority Targets #### 6. GPIO Antenna Bypass The dual-antenna system uses GPIO lines 3 and 7 on `gpiochip1`. If the antenna selection logic can be influenced by QR scan data, an attacker could potentially read from or interfere with the RF path. #### 7. Audit Counter Manipulation Audit counters (`AUD_CONNECTIVITY_BAD_QR_SCANS`, `AUD_CONNECTIVITY_USERS_SCANNED`, etc.) are tracked in shared memory or audit logs. If the QR parser crashes before incrementing error counters, brute-force scanning of QR codes could go undetected. #### 8. Node4 Firmware Update Path The `node4` bridge board has an LPC812/LPC1124 MCU that can be firmware-updated over the nodebus. If the update path is triggered by a crafted nodebus message, an attacker could flash malicious firmware that intercepts or modifies QR scan data. ### Low Priority / Design Notes #### 9. `/tmp/qrx_.bmp` Race Condition The QR code BMP is written to `/tmp/qrx_.bmp` — a predictable path in a world-writable directory. A local attacker could replace the QR code image between generation and display, but this requires local code execution on the Raspberry Pi. #### 10. Pre-Generated QR Code Assets The game includes pre-generated QR code images: - `assets/nuk/images/Connectivity/QrCodes/quick_registration_qr_code.png` - `assets/nuk/images/Connectivity/Icons/ScanQR/ScanQR.png` These are imported into Godot but have **no script or scene references** — they are orphaned assets. The `quick_registration_qr_code.png` is a static QR code that may encode a fixed URL or machine identifier. ## Audit Counter Strings | Offset | String | Purpose | |---|---|---| | `0x2594d60` | `AUD_CONNECTIVITY_USERS_SCANNED` | Users scanned via QR | | `0x2594d80` | `AUD_CONNECTIVITY_USERS_JOINED` | Users joined via QR | | `0x2594e88` | `AUD_CONNECTIVITY_BAD_USER_SCANS` | Failed user QR scans | | `0x2594ea8` | `AUD_CONNECTIVITY_BAD_QR_SCANS` | Failed QR scans overall | | `0x2594ec8` | `AUD_CONNECTIVITY_NET_CONNECTION_BAD_ACTION` | Bad network action | | `0x25964b0` | `AUD_RULE_AREA_DISCOVER_POKEMON_SCAN_STARTED` | In-game scan started | | `0x25964e0` | `AUD_RULE_AREA_DISCOVER_POKEMON_SCANNED` | In-game scan completed | | `0x2596508` | `AUD_RULE_AREA_DISCOVER_POKEMON_SCAN_FAILED` | In-game scan failed | ## Godot UI Classes | Class Name | Offset | Role | |---|---|---| | `NetPlayQrCode` | `0x25db058` | QR code display in network play | | `NetWrongQrOverlay` | `0x25d9d20` | Error overlay for invalid QR | | `MenuPageQrAdjustments` | `0x25ff0e8` | Menu page for QR adjustments | | `QrRC` | `0x280770f` | QR resource class (abbreviated) | ## Diagnostic Messages | Offset | String | |---|---| | `0x25dd3e8` | `Check QR scanner RJ45 nodebus cable` | | `0x25dd410` | `Check QR scanner ribbon cable` | | `0x25a85e0` | `Pull Interlock Switch To Power QR Scanner` | | `0x25a5528` | `This login QR code is no longer valid` | | `0x25a5578` | `This machine is not registered with Insider Connected` | ## Key Differences from conagent The QR code system is **entirely separate** from `conagent`: | Aspect | conagent | QR Scanner (game binary) | |---|---|---| | Binary | `/games/pokemon_pro/conagent` | `/games/pokemon_pro/game` | | Role | Backend connectivity agent | Main game process | | QR code gen | None | Uses `qrcodegen` library | | QR code scan | None | Via hardware scanner + node4 bridge | | WiFi config | No | Yes, via `wpa_supplicant` | | User auth | API token/device auth | QR-based player UUID scan | ## Follow-up Targets 1. **Binary-level analysis**: Use Ghidra to decompile: - `QrOfflineParser::parse` (find via vtable xref from `0x25fe1c0`) - `NetworkQrParser::parse` (find via vtable xref from `0x25fe218`) - The `REG:T:WPA` string splitting and SSID/PSK extraction code - The `wpa_cli` control interface invocation site - The serial bridge read/parse loop at VA `0x94b9d0` 2. **Dynamic testing**: If hardware or emulation is available: - Fuzz the serial bridge protocol with crafted STX/ETX/ESC frames - Test `REG:T:WPA` with SSID/PSK containing shell metacharacters - Test `REG:T:WPA` with oversized SSID (>256 bytes) for buffer overflow - Test RapidJSON parsing with malformed JSON in QR code content 3. **Node4 firmware analysis**: Disassemble `node4-LPC812-1_33_0.hex` and `node4-LPC1124_303-1_33_0.hex` as ARM Thumb to: - Recover the UART baud rate configuration - Understand the scanner module command passthrough protocol - Verify the firmware update mechanism 4. **Physical inspection**: If hardware is available: - Identify the scanner module model from the PCB - Check if the scanner module has its own debug/firmware update port - Verify the baud rate and protocol between node4 MCU and scanner module 5. **Nodebus security audit**: Verify the XOR encryption scheme for secure nodebus commands and assess whether it can be bypassed to inject forged QR scan results.