14 KiB
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— overviewev_c3719a85…, batch decompilesev_7ee0b809…(protocol/auth/command) andev_99e67015…(startup/config/sequencer), RmiContext bytesev_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
updatestring-command intent. service_control.xml: no reader found in this binary.