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
entrydecompilation shows__libc_start_main(&DAT_00103d90, ...). - Ghidra
find_code_gapsreported one large unanalyzed region from00104411to0012f11f, which covers most application logic. - Local
objdump -pshows the executable LOAD segment maps file offsets directly to VAs from0x0through0x41007, and Ghidra imported it with an image base of0x100000.
Because of that analysis limitation, this report combines:
- Ghidra import/import-symbol results.
- Local
objdumpdisassembly. - Local
strings -a -t xevidence. - 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.log0x36748:/dump/log/connectivity/agent.msglog0x36770:/dump/log/connectivity/agent.httplog
Inferred architecture:
- The game process communicates with
conagentover a local Unix-domain socket. conagentparses 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.4libssl.so.3libcrypto.so.3libstdc++.so.6libm.so.6libgcc_s.so.1libc.so.6ld-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_mainwith the main function pointer at0x3d90. - The main function allocates a stack configuration block of about
0x140bytes. - It installs signal handling via
sigaction. - It calls internal setup around
0x23960, then creates a logger/channel around0x24310. - 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
0xc990with ausleep(16000)delay until a global shutdown flag is set. - It shuts down the interface around
0xc8d0, destroys logging around0x23cb0, and returns0.
Confirmed configuration strings:
0x36888:Configuration data:0x368a0:Log path : %s0x368c0:Message log path : %s0x368e0:HTTP log path : %s0x36900:Game process host : %s0x36920:Socket type : %s0x36940:Log level : %s0x36960:Transmit buffer size : %zu (%zuKB)0x36988:HTTP request count : %zu0x369a8:Log received messages: %s0x369c8:Log HTTP data : %s0x369e8:Log to console : %s0x36a08:Silent operation : %s
Local Game Protocol
Source-string evidence:
0x36ac0:src/agent_client.cpp0x36db0:Client receive thread started normally.0x36f08:client_rx0x37010:agent_client_rx0x37290: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:
AgreeProtocolVersionAcceptConfigDenyConfigCommsEnableCommsDisableRegisterMachineAuthPlayerPingServerDisableNetworkInterfaceConfigureNetworkInterfaceCheckForUpdatesQueryServerTimeQueueFileTransferUpdateTransferStateQueryTransferStatusQueryTransferEngineStatusQueryAchievementDescriptorsGameToServerPOSTGameToServerGET
State names:
AwaitRegistrationCommsDisabledNotConnectedNegotiateProtocolAwaitConfigCommsEnabled
Protocol/config message names:
HEARTBEATCONFIG_SECURITYCONFIG_NETWORKCONFIG_TITLECONFIG_DIAGNOSTICSCONFIG_ACCEPTCONFIG_DENYCOMMS_ENABLECOMMS_DISABLECOMMS_REJECTSYSTEM_STATUSMACHINE_REGISTERSET_MACHINE_UUIDPING_REQUESTPING_RESULTSERVER_RESPONSEPLAYER_AUTHENTICATEGAME_ACHIEVEMENT_QUERYDISABLE_INTERFACE_REQUESTCONFIGURE_INTERFACE_RESULTSOFTWARE_VERSION_INFOFILE_TRANSFER_REQUESTFILE_TRANSFER_STATUSFILE_TRANSFER_QUERYFILE_TRANSFER_UPDATEGAME_TO_SERVER_POSTGAME_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 %sDevice-Token: %sDevice-Refresh: %sMessage-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_numberto 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/conagentreports the URL format at file/string offset0x35618.- Ghidra xref from string address
00135618identifiesFUN_00115930; the decompiler showssrc/client_http.cppconstructing the URL withsnprintf(..., "http://%s:8345/b8Ag3XU6TH/wibbly_wobbly_timey_wimey/", server_host). FUN_00115930is thehttp_client_query_server_timepath. It requires a destination buffer and backend server hostname/IP, optionally binds a network interface, sets libcurl options, uses user agentSpike-Connectivity-Agent/1.0, performs the request, stores the HTTP status, and copies up to0x1000bytes into the response buffer.- The caller in
src/client_game.cpplogsAttempting 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_REQUESTmessage from../source/net/net_connection.cppand logsRequesting 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 ifserver_timezoneis present and notUTC. If no data is returned, it logsTODO(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_numbermachine_configuration_numbermodel_numbermodel_versionuser_uuidmac_addressinternal_ip_address
Machine authentication request fields:
machine_uuidmachine_configuration_numbercode_version
Player authentication request fields:
user_uuidmachine_uuid
Game auth response fields:
stern_cdn_urlstern_speed_test_url
The binary contains detailed service status strings such as:
MACHINE_NOT_ENABLEDMACHINE_NOT_REGISTEREDMACHINE_HAS_BEEN_ARCHIVEDMACHINE_PENDING_HARDWARE_REPLACEMENTGAME_MACHINE_TOKEN_INVALIDGAME_MACHINE_TOKEN_EXPIREDGAME_MACHINE_TOKEN_REVOKEDGAME_MACHINE_TOKEN_MISMATCHUSER_NOT_ACTIVEUSER_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.%s0x2f888:iface %s inet dhcp0x2f8f0:iface %s inet static0x2f909:address %s0x2f919:netmask %s0x2f929:gateway %s0x2f939:dns-nameservers %s %s0x2f951:dns-nameservers %s0x2fa78:ifdown -i %s %s0x31d70: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
APPLYorSTORE. - Required interface MAC address.
Security note:
- The binary imports
system. - The network code constructs
ifdown -i %s %sandifup -i %s %scommand 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.com0x365e8:Logs_20140328.PutLogEvents0x36268:AWS4-HMAC-SHA2560x36280:/us-west-1/logs/aws4_request0x361b9:host:logs.us-west-1.amazonaws.com0x36478:src/cloudwatch_logger.cpp0x36498:nextSequenceToken0x364c8:UnrecognizedClientException0x364e8: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:
keystokenssternmachinesternapi
This report does not include any secret values. No concrete AWS access keys were copied into this page.
Security-relevant Observations
conagentis root-supervised by/etc/init.d/conagent_monitor, which restarts it forever.- It accepts commands from the local game process over
/usr/local/spike/agent.uds. - It can write network interface config files and execute
ifup/ifdownthrough shell command strings. - It receives game-originated direct HTTP GET/POST proxy requests, but strings show endpoint and payload validation/NACK paths.
- It handles auth tokens, device tokens, refresh tokens, and CloudWatch credentials in-process.
- It logs HTTP and message traffic to
/dump/log/connectivity/agent.*depending on configuration. - 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_filefor/Users/jordan/Downloads/pokemon_spike3_rootfs/games/pokemon_pro/conagentlist_importsfind_code_gapsdecompile_functionat00104300
Follow-up Targets
- Force Ghidra function creation for the large stripped
.textregion, then rerun decompilation on:- Main at file VA
0x3d90/ Ghidra VA0x103d90. - Logger creation/destruction around
0x23960and0x23cb0. - Agent client creation/update/shutdown around
0xc2c0,0xc940,0xc990, and0xc8d0. - HTTP client functions around the
src/client_http.cppstring cluster.
- Main at file VA
- 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.