Files
pokemon_pinball_wiki/qr-code-analysis.md

18 KiB
Raw Blame History

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 0x25db2700x25db2e0):

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 0x94b9d00x94bbfc)

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 0x25b8a400x25b8c00:

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 0x25fe1c00x25fe2d0:

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 0x25d63480x25d6f20 (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.