Files
pokemon_pinball_wiki/conagent-re.md
2026-07-06 20:06:06 -05:00

16 KiB

Conagent Reverse Engineering Report

Scope

Target binary:

  • Target path: /games/pokemon_pro/conagent
  • Workspace path: games/pokemon_pro/conagent
  • SHA-256: a76da91f0cfdaecceb579ef27d223ab8a86c313d0eefa5217b96823c43c97e69
  • Type: stripped AArch64 PIE ELF, dynamically linked, interpreter /lib/ld-linux-aarch64.so.1
  • Size: 272,432 bytes
  • Ghidra import path: /pokemon_spike3/conagent
  • Ghidra language: AARCH64:LE:64:v8A

Ghidra Status

The binary imported and auto-analyzed in Ghidra successfully. The MCP decompiler recovered the ELF entry stub, but Ghidra did not create ordinary functions for most of the stripped .text region during this pass.

Evidence:

  • Ghidra import result: success, language AARCH64:LE:64:v8A, auto-analysis complete.
  • Ghidra entry decompilation shows __libc_start_main(&DAT_00103d90, ...).
  • Ghidra find_code_gaps reported one large unanalyzed region from 00104411 to 0012f11f, which covers most application logic.
  • Local objdump -p shows the executable LOAD segment maps file offsets directly to VAs from 0x0 through 0x41007, and Ghidra imported it with an image base of 0x100000.

Because of that analysis limitation, this report combines:

  • Ghidra import/import-symbol results.
  • Local objdump disassembly.
  • Local strings -a -t x evidence.
  • Manual reconstruction from string clusters and call sites.

High-level Role

conagent is the Stern Pinball Connectivity Agent. It bridges the local game process to Stern back-end services.

Confirmed evidence:

  • Version/banner string at 0x36838: Stern Pinball Connectivity Agent %s
  • Version string at 0x36818: 1.0.16 (Linux,ARM64,GNU)
  • Library string at 0x39448: libagent 1.0.16 (Linux,ARM64,GNU)
  • Local game-process socket path at 0x396d9: /usr/local/spike/agent.uds
  • Default logs:
    • 0x36720: /dump/log/connectivity/agent.log
    • 0x36748: /dump/log/connectivity/agent.msglog
    • 0x36770: /dump/log/connectivity/agent.httplog

Inferred architecture:

  • The game process communicates with conagent over a local Unix-domain socket.
  • conagent parses framed game messages, maintains a state machine, and translates selected game actions into HTTPS requests and file-transfer activity.
  • It also accepts game-originated network configuration requests and can mutate host network interface configuration.

Dynamic Dependencies and Imports

Dynamic libraries from objdump -p:

  • libcurl.so.4
  • libssl.so.3
  • libcrypto.so.3
  • libstdc++.so.6
  • libm.so.6
  • libgcc_s.so.1
  • libc.so.6
  • ld-linux-aarch64.so.1

Important imported APIs from Ghidra/import table:

  • Network/socket IPC: socket, bind, listen, accept, connect, send, recv, select, shutdown, setsockopt, getaddrinfo, freeaddrinfo
  • HTTP: curl_global_init, curl_easy_init, curl_easy_setopt, curl_easy_perform, curl_easy_getinfo, curl_multi_*, curl_share_*, curl_slist_append
  • Crypto/signing: SHA256_Init, SHA256_Update, SHA256_Final, EVP_sha256, HMAC
  • Filesystem/process: fopen, fread, fwrite, fflush, fclose, rename, unlink, mkdir, stat, readlink, system
  • Shared memory/threading: shm_open, shm_unlink, mmap, munmap, pthread_create, pthread_join, pthread_setname_np, C++ std::thread, std::condition_variable

Startup Flow

Recovered from entry at 0x4300 and main-like range at file VA 0x3d90:

  • ELF entry calls __libc_start_main with the main function pointer at 0x3d90.
  • The main function allocates a stack configuration block of about 0x140 bytes.
  • It installs signal handling via sigaction.
  • It calls internal setup around 0x23960, then creates a logger/channel around 0x24310.
  • It logs banner/configuration strings from agent.cpp.
  • It creates the game process interface around 0xc2c0.
  • It starts the game client/interface around 0xc940.
  • It loops calling an update function around 0xc990 with a usleep(16000) delay until a global shutdown flag is set.
  • It shuts down the interface around 0xc8d0, destroys logging around 0x23cb0, and returns 0.

Confirmed configuration strings:

  • 0x36888: Configuration data:
  • 0x368a0: Log path : %s
  • 0x368c0: Message log path : %s
  • 0x368e0: HTTP log path : %s
  • 0x36900: Game process host : %s
  • 0x36920: Socket type : %s
  • 0x36940: Log level : %s
  • 0x36960: Transmit buffer size : %zu (%zuKB)
  • 0x36988: HTTP request count : %zu
  • 0x369a8: Log received messages: %s
  • 0x369c8: Log HTTP data : %s
  • 0x369e8: Log to console : %s
  • 0x36a08: Silent operation : %s

Local Game Protocol

Source-string evidence:

  • 0x36ac0: src/agent_client.cpp
  • 0x36db0: Client receive thread started normally.
  • 0x36f08: client_rx
  • 0x37010: agent_client_rx
  • 0x37290: Message stream state reset; prev ID %d, next ID %d.
  • 0x373d0: Expected ID %d; got ID %d. Sending stream RESET.
  • 0x37408: Empty receive queue but ACTION_MESSAGE received.

The local protocol appears to be a reliable framed message stream with:

  • Message IDs.
  • ACK/NACK handling.
  • Resend handling.
  • Stream reset handling.
  • A receive thread named agent_client_rx.

Action names:

  • AgreeProtocolVersion
  • AcceptConfig
  • DenyConfig
  • CommsEnable
  • CommsDisable
  • RegisterMachine
  • AuthPlayer
  • PingServer
  • DisableNetworkInterface
  • ConfigureNetworkInterface
  • CheckForUpdates
  • QueryServerTime
  • QueueFileTransfer
  • UpdateTransferState
  • QueryTransferStatus
  • QueryTransferEngineStatus
  • QueryAchievementDescriptors
  • GameToServerPOST
  • GameToServerGET

State names:

  • AwaitRegistration
  • CommsDisabled
  • NotConnected
  • NegotiateProtocol
  • AwaitConfig
  • CommsEnabled

Protocol/config message names:

  • HEARTBEAT
  • CONFIG_SECURITY
  • CONFIG_NETWORK
  • CONFIG_TITLE
  • CONFIG_DIAGNOSTICS
  • CONFIG_ACCEPT
  • CONFIG_DENY
  • COMMS_ENABLE
  • COMMS_DISABLE
  • COMMS_REJECT
  • SYSTEM_STATUS
  • MACHINE_REGISTER
  • SET_MACHINE_UUID
  • PING_REQUEST
  • PING_RESULT
  • SERVER_RESPONSE
  • PLAYER_AUTHENTICATE
  • GAME_ACHIEVEMENT_QUERY
  • DISABLE_INTERFACE_REQUEST
  • CONFIGURE_INTERFACE_RESULT
  • SOFTWARE_VERSION_INFO
  • FILE_TRANSFER_REQUEST
  • FILE_TRANSFER_STATUS
  • FILE_TRANSFER_QUERY
  • FILE_TRANSFER_UPDATE
  • GAME_TO_SERVER_POST
  • GAME_TO_SERVER_GET

Back-end API Behavior

The agent talks to HTTPS APIs with libcurl and an API token.

Confirmed endpoints and strings:

Offset String
0x33a20 https://%s/api-token-auth/
0x33a40 {"username":"%s","password":"%s"}
0x301f8 /api/v1/ping
0x30388 /api/v1/game/version_upgrades_available
0x30818 /api/v3/game/player_auth
0x30c60 /api/v3/game/game_register
0x311a8 /api/v3/game/game_achievement_descriptors
0x31750 /api/v3/game/game_auth
0x33190 /api/v2/game/heartbeat
0x35618 http://%s:8345/b8Ag3XU6TH/wibbly_wobbly_timey_wimey/
0x35cb0 https://%s%s/?%.*s
0x35cc8 https://%s%s/

HTTP request headers/fields:

  • Authorization: Token %s
  • Device-Token: %s
  • Device-Refresh: %s
  • Message-Number:
  • App-Status-Code:
  • Content-Type: application/json
  • User agent: Spike-Connectivity-Agent/1.0

The token sync flow:

  • Requires server hostname/IP, API token username, and API token password.
  • POSTs JSON credentials to https://<server>/api-token-auth/.
  • Parses a JSON object and extracts string field token.
  • Logs detailed cURL and HTTP failures.

The generic request flow:

  • Builds HTTPS URLs from server hostname, endpoint, and optional query arguments.
  • Adds message_number to API requests.
  • Adds token/device headers.
  • Supports at least DELETE, POST, PUT, and default GET behavior.
  • Reads server-updated message numbers from response data/headers.

Server Time Probe Endpoint

/games/pokemon_pro/conagent contains a separate plain-HTTP server-time probe:

http://%s:8345/b8Ag3XU6TH/wibbly_wobbly_timey_wimey/

Evidence:

  • strings -a -t x /games/pokemon_pro/conagent reports the URL format at file/string offset 0x35618.
  • Ghidra xref from string address 00135618 identifies FUN_00115930; the decompiler shows src/client_http.cpp constructing the URL with snprintf(..., "http://%s:8345/b8Ag3XU6TH/wibbly_wobbly_timey_wimey/", server_host).
  • FUN_00115930 is the http_client_query_server_time path. It requires a destination buffer and backend server hostname/IP, optionally binds a network interface, sets libcurl options, uses user agent Spike-Connectivity-Agent/1.0, performs the request, stores the HTTP status, and copies up to 0x1000 bytes into the response buffer.
  • The caller in src/client_game.cpp logs Attempting to retrieve current date and time from server %s., calls the HTTP helper, and returns either the response body or an empty response to the game process.
  • The game binary builds a ServerTime / TIME_QUERY_REQUEST message from ../source/net/net_connection.cpp and logs Requesting Agent to retrieve server time of day..
  • The game-side response handler parses the returned body as JSON, reads datetime, applies it to TimeOfDay, and warns if server_timezone is present and not UTC. If no data is returned, it logs TODO(Game): The server did not respond to the time query request.

Interpretation: this is not one of the authenticated HTTPS /api/v* calls. It is a special connectivity-agent time endpoint on port 8345, apparently used so the game can ask the configured server for current UTC time and update its internal TimeOfDay. The path token b8Ag3XU6TH/wibbly_wobbly_timey_wimey appears to be obscurity for a narrow time-service endpoint, not a secret copied into the wiki from a credential file.

Game Registration and Authentication

Machine registration request fields:

  • serial_number
  • machine_configuration_number
  • model_number
  • model_version
  • user_uuid
  • mac_address
  • internal_ip_address

Machine authentication request fields:

  • machine_uuid
  • machine_configuration_number
  • code_version

Player authentication request fields:

  • user_uuid
  • machine_uuid

Game auth response fields:

  • stern_cdn_url
  • stern_speed_test_url

The binary contains detailed service status strings such as:

  • MACHINE_NOT_ENABLED
  • MACHINE_NOT_REGISTERED
  • MACHINE_HAS_BEEN_ARCHIVED
  • MACHINE_PENDING_HARDWARE_REPLACEMENT
  • GAME_MACHINE_TOKEN_INVALID
  • GAME_MACHINE_TOKEN_EXPIRED
  • GAME_MACHINE_TOKEN_REVOKED
  • GAME_MACHINE_TOKEN_MISMATCH
  • USER_NOT_ACTIVE
  • USER_TOKEN_EXPIRED

File Transfer and Software Update Behavior

Evidence strings under src/client_http.cpp:

  • Launching download request %lu, priority %u, %s -> %s.
  • Launching upload request %lu, priority %u, %s -> %s.
  • File transfer request %lu has type %s, which is unsupported. The request will be dropped.
  • File transfer %lu failed; partially downloaded file %s will be deleted.
  • All software update file transfers will be paused and all other transfers will be canceled during the next http_client_update.
  • Found transfer request %lu as the active software update transfer.
  • Found transfer request %lu in the software update queue.

Recovered behavior:

  • File transfers are managed through libcurl multi handles.
  • Transfers are prioritized into high-priority, normal-priority, software-update, and paused queues.
  • Download destinations are opened/created on disk.
  • Upload sources are opened and sized with file seeks.
  • Partial downloads are deleted on failure.
  • File transfer queue size and request size are configurable through CONFIG_NETWORK.

This likely feeds the /connectivity/update/UPDATE_REQUESTED flow documented in update-and-recovery.md, but that handoff was not proven in this pass.

Network Configuration Mutation

The agent can apply game-requested network configuration to the host.

Evidence strings:

  • 0x30448: /tmp/network_interface.%s
  • 0x2f888: iface %s inet dhcp
  • 0x2f8f0: iface %s inet static
  • 0x2f909: address %s
  • 0x2f919: netmask %s
  • 0x2f929: gateway %s
  • 0x2f939: dns-nameservers %s %s
  • 0x2f951: dns-nameservers %s
  • 0x2fa78: ifdown -i %s %s
  • 0x31d70: ifup -i %s %s

Validation strings indicate it checks for:

  • Interface name.
  • IP address.
  • Netmask.
  • Gateway.
  • Primary DNS for static config.
  • Server hostname.
  • Transfer queue capacity.
  • Transfer request size.
  • Flags APPLY or STORE.
  • Required interface MAC address.

Security note:

  • The binary imports system.
  • The network code constructs ifdown -i %s %s and ifup -i %s %s command lines.
  • The strings indicate validation exists, but this pass did not prove whether interface names and generated config paths are shell-escaped or restricted enough. This is a worthwhile follow-up audit target.

CloudWatch Logging

The binary contains an AWS CloudWatch Logs client.

Evidence strings:

  • 0x362b0: https://logs.us-west-1.amazonaws.com
  • 0x365e8: Logs_20140328.PutLogEvents
  • 0x36268: AWS4-HMAC-SHA256
  • 0x36280: /us-west-1/logs/aws4_request
  • 0x361b9: host:logs.us-west-1.amazonaws.com
  • 0x36478: src/cloudwatch_logger.cpp
  • 0x36498: nextSequenceToken
  • 0x364c8: UnrecognizedClientException
  • 0x364e8: CLOUD LOG: disabling due to credential errors

The implementation imports OpenSSL SHA-256 and HMAC routines. It appears to build AWS Signature Version 4 requests for PutLogEvents.

Credential-related string labels:

  • keys
  • tokens
  • sternmachine
  • sternapi

This report does not include any secret values. No concrete AWS access keys were copied into this page.

Security-relevant Observations

  1. conagent is root-supervised by /etc/init.d/conagent_monitor, which restarts it forever.
  2. It accepts commands from the local game process over /usr/local/spike/agent.uds.
  3. It can write network interface config files and execute ifup/ifdown through shell command strings.
  4. It receives game-originated direct HTTP GET/POST proxy requests, but strings show endpoint and payload validation/NACK paths.
  5. It handles auth tokens, device tokens, refresh tokens, and CloudWatch credentials in-process.
  6. It logs HTTP and message traffic to /dump/log/connectivity/agent.* depending on configuration.
  7. File transfers can write to local paths and delete partial downloads on failure; path validation needs a dedicated pass.

Generated C-like Export

See conagent-pseudocode.c in this wiki folder. It is a manually reconstructed, non-compilable pseudocode export based on the Ghidra import, local disassembly, and string evidence.

Commands Used

file games/pokemon_pro/conagent
shasum -a 256 games/pokemon_pro/conagent
objdump -p games/pokemon_pro/conagent
nm -D games/pokemon_pro/conagent
strings -a -t x -n 4 games/pokemon_pro/conagent
objdump -d --start-address=0x3d90 --stop-address=0x42b0 games/pokemon_pro/conagent
objdump -d --start-address=0x23200 --stop-address=0x23d00 games/pokemon_pro/conagent

Ghidra MCP calls:

  • import_file for /Users/jordan/Downloads/pokemon_spike3_rootfs/games/pokemon_pro/conagent
  • list_imports
  • find_code_gaps
  • decompile_function at 00104300

Follow-up Targets

  • Force Ghidra function creation for the large stripped .text region, then rerun decompilation on:
    • Main at file VA 0x3d90 / Ghidra VA 0x103d90.
    • Logger creation/destruction around 0x23960 and 0x23cb0.
    • Agent client creation/update/shutdown around 0xc2c0, 0xc940, 0xc990, and 0xc8d0.
    • HTTP client functions around the src/client_http.cpp string cluster.
  • Audit shell command construction for ifup/ifdown.
  • Audit direct Game-to-Server endpoint validation.
  • Audit file-transfer local path validation and update handoff into /connectivity/update.
  • Determine where API token username/password and device token material originate in runtime config messages.