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

140 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.