server-re/decomp/AgentManager_Release_x64/evidence.md
2026-10-15 09:58:24 -04:00

14 KiB
Raw Blame History

AgentManager_Release_x64.exe — finding ledger

  • SHA-256 8fd8ca91725858651a7839d34257b10c2970ac16c97e60c523b8b0c983fcb186, 1,584,128 bytes
  • PE32+ x86-64, Windows CUI, MSVC linker 10.0 (VS2010; imports MSVCR100/MSVCP100)
  • PDB reference F:\work\Project\Output\exe\AgentManager_Release_x64.pdb (GUID cc211b01-ac46-4192-9b64-b1d16ba71956, age 1) — PDB not available
  • Imports: WS2_32, MSWSOCK, WINMM, KERNEL32, USER32, ADVAPI32, OLEAUT32, MSVCP100, RPCRT4, MSVCR100, BugTrapU-x64, ole32, dbghelp
  • Ghidra 12.1.2 via REA 6.5.0; 7,400 procedures, 1,613 defined strings (ev_c3719a85…)
  • Evidence: evidence-bundle.json — overview ev_c3719a85…, batch decompiles ev_7ee0b809… (protocol/auth/command) and ev_99e67015… (startup/config/sequencer), RmiContext bytes ev_3fe7cbab…. Static analysis only; nothing here was observed at runtime.

Status: analysis complete for this pass (protocol, auth, config sources, command semantics, startup sequencer). REA session closed; porting.md intentionally skipped.

Language / libraries

C++ (MSVC RTTI .?AV…, MBISingleton<T> templates, __CxxFrameHandler3, MSVCP100). Same Magi* framework as the other servers (MagiServerBase, MagiServerOption, MagiCustomOption, MagiServerFile, MagiXmlFormat, MagiStatistics, MBCLog, Magi::Timer) — see docs/SHARED_FRAMEWORK.md. Third-party (all already in third-party/README.md, no new libraries): ProudNet (incl. VizC2S/VizS2C visualizer RMIs), TinyXML 2.5.x (TiXml*), BugTrap, ATL/_com_error + ADO (sqloledb).

Outputs

Path Content
full/functions/<addr>.c, full/index.tsv Ghidra pseudocode, 6,993 non-thunk functions (1 failed: 0x1400203d0, overlapping varnodes)
full/vtables.tsv 1,441 RTTI vftable slots → functions
<addr>.pseudo.c REA pseudocode for the 19 functions in ev_7ee0b809… and ev_99e67015…
binary_overview.json REA overview record

Findings

# Conclusion Type Evidence
1 Central hub between Agents (one per machine) and AgentMonitor (operator tool, presumably SMMonitoring): ProudNet server with two stub/proxy pairs: AGENT2AGENTMANAGER stub + AGENTMANAGER2AGENT proxy (class MagiAgentManagerNetwork), AGENTMONITOR2AGENTMANAGER stub + AGENTMANAGER2AGENTMONITOR proxy (class MagiAgentManagerMonitorNetwork) Observed (RTTI, vftables); "SMMonitoring = AgentMonitor" is inference full/vtables.tsv
2 Agent→AgentManager RMIs 0x8599–0x85a3 (table below); dispatcher 0x140026520, handlers = MagiAgentManagerNetwork vftable 0x14012f1e8 slots 7–17 Observed 0x140026520.pseudo.c
3 AgentMonitor→AgentManager RMIs 0x8661–0x866c; dispatcher 0x14002c3c0, handlers = vftable 0x14012f6d8 slots 7–18 Observed 0x14002c3c0.pseudo.c
4 Outgoing: AgentManager→Agent 0x85fd–0x860d, AgentManager→Monitor 0x86c5–0x86d2 Observed (proxy functions) full/functions/0x140023930.c … 0x14002c230.c
5 Monitor auth (REQ_AUTH → 0x140015290): logs MONITOR AUTH : ID[%s], calls dbo.up_auth_agentaccount(@Account,@Password) via ADO (0x140009d00), reads column permission; permission < 1 → fail; else sends RESP_AUTH, server-option list and per-server info to the monitor Observed 0x140015290.pseudo.c, 0x140009d00.pseudo.c; SP in decomp/schema/RustyHearts_Auth/procs/up_auth_agentaccount.sql (plaintext compare against dbo.AgentAccount)
6 DB connection strings come from the option file (finding 11); the ADO layer detects PROVIDER=/DRIVER= and appends ;Application Name= (0x140074210) Observed full/functions/0x140074210.c
7 PT_SERVER_COMMAND handler (0x140015ac0): refuses with Not Active Server.!!! unless option byte +0x180 set; command 4 = patch: launches a configured executable (server-file entry type 0x18) with CreateProcessW(…, CREATE_NEW_CONSOLE …), logs FAILED PATCH ON %s; commands 0–3,5–7,0xd forwarded to agents (FUN_14000cb70); 0xc sets a flag at +0x9c Observed; full code table in §Server commands 0x140015ac0.pseudo.c
8 PT_SERVER_REGISTER handler (0x1400137f0) stores agent/server info by server id; type 0x0d entries get special handling (marks registered, pushes info to monitors) Observed; 0x0d = Agent in the server-kind enum (docs/CONFIG.md §5) 0x1400137f0.pseudo.c
9 Single instance via mutex Global\RustyHearts_AgentManager (same pattern as Agent: ACCESS_DENIED/ALREADY_EXISTS → print, sleep 3 s, fail) Observed 0x14001d850.pseudo.c
10 Option files actually used: option\agentmanager_serveroption.xml (main, path overridable by argv[1]), option\customoption.xml (0x140038530), option\agent.xml (0x140033ff0, <server> list), option\total_option.xml (only when argv[1] is numeric, agent mode, 0x140030300/0x14002fe40), option\patch.xml (written, not read: 0x140034220 "Create Patch Option File Succeed"). service_control.xml: string only, no reader found. Log Log\AgentManagerServer.log, protocol log Log\Protocol_AgentManager Observed 0x140001390, 0x140002400, 0x14002fc00
11 DB config: standalone loader 0x140031000 reads flat <db_account string timeout/> (→ +0x90/+0x130) and <db_game_log …/> (→ +0x108/+0x13c) (plus db_web, db_game, ignored here). Then 0x140032f70 re-reads the same file for <db><agentmanager><db_account/><db_game_log/></agentmanager></db> and overrides both; if absent it prints DB PASE ERROR !!! but continues (non-fatal). The shipped sample uses the flat form, so the override path fails and flat values are used (I for the shipped effect) Observed 0x140031000, 0x140032f70.pseudo.c, 0x140002400.pseudo.c
12 db_account connection (→ RustyHearts_Auth) is used at startup for: up_read_agentmanager(@code) (own ServerOption row), up_read_all_serveroption, up_read_serverfile, up_read_realtimeevent, and later up_auth_agentaccount. db_game_log connection (→ RustyHearts_Log) is used for up_insert_log_monitor_{command,user,notice}, up_insert_log_pvp_ladder, up_create_delete_table. Each failed read prints … Failed !!!, sleeps 3 s and clears the run flag Observed (SP names, objects +0x49460/+0x49468); DB mapping from SP location in schema 0x140002790.pseudo.c, 0x140007a20.pseudo.c, decomp/schema/
13 Listen ports: XML <server private_ip private_port public_ip public_port …> gives initial values, but up_read_agentmanager row whose Code == server.code overrides them (0x14002fe40: World, Type, Private/PublicAddress, Private/Public/Udp port, UdpCount). Agent listener (0x140005460) binds PrivateAddress:PrivatePort (+0x30/+0x80); monitor listener (0x140006380, log MONITOR NETWORK INIT : ADDRESS[%s] PORT[%u]) binds PublicAddress:PublicPort (+0x58/+0x84). Shipped XML: 51091 / 50091; Agent's agent.xml <agentmanager port="51091"> matches Observed (code); sample values (S) 0x140007a20.pseudo.c, 0x14002fe40.pseudo.c, 0x140005460.pseudo.c, 0x140006380.pseudo.c
14 REQ_AGENT_UPDATE/SERVER_ON/AUTO handlers (0x1400166d0/0x1400169c0/0x140016cb0) send PT_STRING_COMMAND (proxy slot 26, 0x8607) with literal "update"/"serveron"/"auto" to the addressed agent. On the Agent (Agent:0x140005950): serveron → start all its servers (Agent:0x140009890); auto → toggle global auto-revive (+0x171) and report cmd 7; update → iterates its server list and re-reports it (I) Observed (Manager); Agent side observed except update intent 0x1400169c0.pseudo.c, decomp/Agent_Release_x64/full/functions/0x140005950.c
15 PT_PATCH_SERVER / PT_BACKUP_CHARACTER_TABLE_COUNT/_END from agents (slots 15–17) are relayed to monitors (0x140011840/0x140011ba0/0x140011cc0 → monitor proxy) Observed full/functions/0x140013f80.c … 0x140013fd0.c
16 Console hotkeys (0x140003420, CConsoleControl): e.g. 0x32 CreateBackupTable → up_create_delete_table, 0x33 toggle Active (+0x180), 0x34 reload ServerFile, 0x35 reload ServerOption, print on/off Observed 0x140003420.pseudo.c
17 RMI payload encryption: every proxy call passes one shared RmiContext DAT_14016f5d0 whose static image holds only 1 in its first field and zeros elsewhere; no secure-context variant is referenced. Inference: messages are sent with ProudNet's default (unencrypted) mode; runtime initializers or server start parameters could still change this — unknown Observed bytes / inference ev_3fe7cbab…

Server commands (PT_SERVER_COMMAND monitor → manager → agent)

Gate: AgentManager ignores all of these unless its Active flag (+0x180, console hotkey 0x33) is set.

Code AgentManager (0x140015ac0) Agent (Agent:0x14000e1e0)
0 forward server on: launch target server (Agent:0x140008ea0)
1 forward kill target server by PID (OpenProcess+TerminateProcess, Agent:0x140009720)
2, 5, 6, 0xd forward forwarded to the game server as AGENT2SERVER PT_SERVER_COMMAND (0x84d2); meaning is server-side. On DBCServer (DBC:0x140153f00): 1/2/5/6/8 set flags, 0xd toggles a flag (+0xd), 7 toggles auto-revive. That 0xd triggers the character-table backup is inference (adjacent PT_BACKUP_CHARACTER_TABLE_* RMIs and string CharacterTable BackUp.)
3 forward log REQ_SERVER_KILL, then same as 1
4 patch: if no patch server registered, writes option\patch.xml, launches ServerFile type 0x18 (PatchServer, not shipped) with CREATE_NEW_CONSOLE; logs FAILED PATCH ON %s on failure; then forwards turns global auto-revive off, launches the patcher, logs SUCCEED PATCH ON %s (Agent:0x14000a080)
7 forward if addressed to the agent's own code: toggle global auto-revive (+0x171)
8, 9, 0xa not forwarded (outside AgentManager's switch) no-op
0xb not forwarded toggle per-server auto-revive (reached only from other paths)
0xc ordered start-all: sets flag, main loop runs sequencer 0x140003d00; not forwarded no-op

Sequencer (0x140003d00, one step per 100 ms tick; odd steps wait until every server of the group reports state 3, even steps send cmd 0 to stopped ones): 1–2 DBC kinds 12/17/18/19 → 3 up_create_delete_table (log DB) → 4 send cmd 0xd to every DBC_GAME (12) → 6–7 MSG/GATE/MANAGER/AUCTION/MATCH (6,8,9,10,11) → 8–9 GM/Guild (7,5) → 10–11 Lobby (1) → 12–13 Dungeon/PvP (2,3) → 14 done.

RMI tables

ID Agent → AgentManager ID Monitor → AgentManager
0x8599 PT_SERVER_REGISTER 0x8661 REQ_AUTH
0x859a PT_SERVER_INFORMATION 0x8662 REQ_SERVER_INFORMATION
0x859b PT_SERVER_COMMAND_RESULT 0x8663 PT_SERVER_COMMAND
0x859c PWE_RESP_RESULT_CODE 0x8664 PT_SERVER_NOTICE
0x859d NOTIFY_STORAGE_ALARM 0x8665 PT_IP_BLOCK
0x859e NOTIFY_MAIL_ALARM 0x8666 PT_CHANNEL_STATUS_CHANGE
0x859f REQ_STATISTICS 0x8667 PT_USER_KICK
0x85a0 PT_MESSAGE 0x8668 PT_STRING_COMMAND
0x85a1 PT_PATCH_SERVER 0x8669 PT_REAL_TIME_EVENT
0x85a2 PT_BACKUP_CHARACTER_TABLE_COUNT 0x866a REQ_AGENT_UPDATE
0x85a3 PT_BACKUP_CHARACTER_TABLE_END 0x866b REQ_AGENT_SERVER_ON
0x866c REQ_AGENT_AUTO
ID AgentManager → Agent ID AgentManager → Monitor
0x85fd PT_AGENT_DATA 0x86c5 RESP_AUTH
0x85fe PT_SERVER_OPTIONS 0x86c6 RESP_SERVER_OPTION
0x85ff PT_SERVER_NOTICE 0x86c7 RESP_SERVER_REGISTER
0x8600 PT_SERVER_COMMAND 0x86c8 RESP_SERVER_INFORMATION
0x8601 PT_SERVER_CONNECT 0x86c9 PT_SERVER_COMMAND_RESULT
0x8602 PT_REFRESH_SERVER_FILE 0x86ca RESP_REGISTER_MASTER
0x8603 PT_REFRESH_SERVER_OPTION 0x86cb RESP_MASTER_SERVER_COMMAND
0x8604 PT_CHANNEL_STATUS_CHANGE 0x86cc RESP_REAL_TIME_EVENT
0x8605 PT_USER_KICK 0x86cd NOTIFY_STORAGE_ALARM
0x8606 PT_CURRENT_USER_COUNT 0x86ce NOTIFY_MAIL_ALARM
0x8607 PT_STRING_COMMAND 0x86cf PT_MESSAGE
0x8608 PT_REAL_TIME_EVENT 0x86d0 PT_PATCH_SERVER
0x8609 PWE_REQ_PLAYER_KICK 0x86d1 PT_BACKUP_CHARACTER_TABLE_COUNT
0x860a PWE_REQ_PLAYER_BAN 0x86d2 PT_BACKUP_CHARACTER_TABLE_END
0x860b PWE_REQ_UPDATE_CASH
0x860c PWE_REQ_INSERT_ITEM
0x860d PWE_REQ_NOTICE

Cross-check (resolved): Agent's AgentManager2AgentStub = 7 base slots + 17 RMI slots; its dispatcher (Agent:0x14001d960) handles exactly 0x85fd–0x860d with the same names. IDs match.

Proposed names (not applied)

Address Name
0x140026520 RNP::Agent2AgentManagerStub::ProcessReceivedMessage
0x14002c3c0 RNP::AgentMonitor2AgentManagerStub::ProcessReceivedMessage
0x140015290 MagiAgentManagerMonitorNetwork::REQ_AUTH
0x140009d00 AuthAgentAccount(account, password, &permission)
0x1400137f0 MagiAgentManagerNetwork::PT_SERVER_REGISTER
0x140015ac0 MagiAgentManagerMonitorNetwork::PT_SERVER_COMMAND
0x14001d850 AcquireSingleInstanceMutex
0x140074210 AdoConnection::BuildConnectionString
0x140001390 wmain
0x140002400 MagiAgentManagerServer::Init (load options)
0x140002790 MagiAgentManagerServer::Run (DB load, networks, main loop)
0x140031000 MagiServerOption::LoadServerXml
0x140032f70 MagiServerOption::LoadAgentManagerDb
0x14002fe40 MagiServerOption::ApplyServerOptionRow
0x140007a20 DbReadAgentManager
0x140005460 MagiAgentManagerNetwork::Start
0x140006380 MagiAgentManagerMonitorNetwork::Start
0x140003d00 StartAllSequencer::Step
0x140034220 WritePatchOptionFile
0x140003420 CConsoleControl::OnKey

Remaining unknowns

  • Exact meaning of codes 2, 5, 6, 0xd on each game-server kind (only DBC's flag handler was checked; its consumer of the 0xd flag was not traced).
  • Whether ProudNet encryption is enabled at runtime (finding 17).
  • Agent update string-command intent.
  • service_control.xml: no reader found in this binary.