18 KiB
QR Code Analysis
Scope
The QR code system on Pokemon Pro involves two separate code paths:
- QR Code Generation — The game uses Nayuki's
qrcodegenlibrary to generate QR codes (WiFi config, Stern Insider Connected login, support URLs) and display them on-screen. - QR Code Scanning — A standalone hardware scanner module connected to the game via a
node4bridge 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:
- Scanner module — A standalone 2D barcode scan engine (OEM part
part:520-9865-00at0x25b8c20). The specific module model (Zebra, Honeywell, Newland, etc.) is not identifiable from strings; the firmware is a thin bridge without scanner-brand identifiers. - Node4 bridge board — A
node4SPIKE-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)
- Connections:
- Ribbon cable from scanner module to node4 board
- RJ45 nodebus cable from node4 to the game system
- GPIO antenna switch: Two RF antennas on
gpiochip1lines 3 (ant1) and 7 (ant2), controlled vialibgpiod.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(ioctl0x541e) /TCSETS2(ioctl0x541f) —struct termios2for 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 bit0x80set - Checksum:
(-sum(body)) & 0xFF, verified before dispatch - Valid commands:
0x10through0x41(checked viasub 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:ant1high,ant2lowarg=2:ant2high,ant1low
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(offset0x25dbe30) — WiFi configuration QR codeREG:T:ETH(offset0x25dbe40) — 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:
NetworkQrParserextracts SSID, PSK, and encryption type- Validation:
"Not a valid WiFi QR code"(0x25a7200) - Game builds
wpa_supplicantconfig block:ssid="%s" scan_ssid=1 key_mgmt=%s (WPA-PSK) pairwise=%s (TKIP or "CCMP TKIP") group=%s psk=%s - Writes to
/etc/wpa_supplicant.conf(0x25dfd68) - Controls WiFi via
wpa_supplicantcontrol interface:ADD_NETWORK(0x25d5688)SET_NETWORK(0x25d5808)ENABLE_NETWORK %s(0x25d5780)CTRL-EVENT-CONNECTED(0x25d57e8)CTRL-EVENT-DISCONNECTED(0x25d57e8)
- Control socket:
/var/run/wpa_supplicant/wlan0(0x25d5708) - Also uses
/usr/sbin/wpa_cli status(0x25e26b8)
Stern Insider Connected QR Data Flow
When a Stern Insider Connected login QR code is scanned:
QrOfflineParserextracts user UUID from QR code- Flow:
QERT_SCANNED_USER_ACCOUNT(0x25dedd0) →ScannedUserAccountUUID(0x25dd568) →AuthenticatePlayer(0x25dd590) - Player UUID is sent to backend via
PLAYER_AUTHENTICATE(0x25ecf18) - 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_cliorwpa_supplicantcontrol interface commands - Write to
/etc/wpa_supplicant.confwith 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
0x1Bsets 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.pngassets/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
-
Binary-level analysis: Use Ghidra to decompile:
QrOfflineParser::parse(find via vtable xref from0x25fe1c0)NetworkQrParser::parse(find via vtable xref from0x25fe218)- The
REG:T:WPAstring splitting and SSID/PSK extraction code - The
wpa_clicontrol interface invocation site - The serial bridge read/parse loop at VA
0x94b9d0
-
Dynamic testing: If hardware or emulation is available:
- Fuzz the serial bridge protocol with crafted STX/ETX/ESC frames
- Test
REG:T:WPAwith SSID/PSK containing shell metacharacters - Test
REG:T:WPAwith oversized SSID (>256 bytes) for buffer overflow - Test RapidJSON parsing with malformed JSON in QR code content
-
Node4 firmware analysis: Disassemble
node4-LPC812-1_33_0.hexandnode4-LPC1124_303-1_33_0.hexas ARM Thumb to:- Recover the UART baud rate configuration
- Understand the scanner module command passthrough protocol
- Verify the firmware update mechanism
-
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
-
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.