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

68 lines
6.5 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.

# Agent_Release_x64.exe — finding ledger
- SHA-256 `6f2be7b8eb799ba51b359d1958720aac5baee5b7ee0d1d36684d3cbd72d45469`, 1,472,512 bytes
- PE32+ x86-64, Windows CUI, MSVC linker 10 (VS2010; imports MSVCR100/MSVCP100)
- PDB reference `F:\work\Project\Output\Exe\Agent_Release_x64.pdb` (GUID a4775113-…, age 1) — PDB not available; names are Ghidra defaults
- Ghidra 12.1.2 via REA 6.5.0; 6,814 procedures, 1,472 defined strings (`ev_9fe3a449…`)
- Full Evidence: `evidence-bundle.json` (14 records). Static analysis only — nothing here was observed at runtime.
## Status: first pass (process-control core). Network protocol and config parsing not yet decompiled.
## Language: C++ (high confidence)
Observed C++-only artifacts: MSVC RTTI type descriptors (`.?AV…@@`, incl. templates
`MBISingleton<T>`, `CFastArray<T>`), vftables, `__CxxFrameHandler3` (C++ EH),
`operator new/delete`, MSVCP100 `std::basic_ostream` imports. Multiple vftables
per class (e.g. `MagiAgentNetwork`, `MGAgentManagerSession`) indicate multiple
inheritance. Statically linked C++ libraries: ProudNet, TinyXML (`TiXml*`), ATL.
## Outputs
Porting spec (Win32 → Linux substitutions, behaviour to reproduce, original bugs): `porting.md`.
| Path | Content |
|---|---|
| `full/functions/<addr>.c` | Ghidra pseudocode for all 6,423 non-thunk functions (1 failed; see `full/index.tsv`). Includes library code (ProudNet, TinyXML, CRT) — not yet labelled. |
| `full/index.tsv` | address, namespace, name, body size, decompile status |
| `full/vtables.tsv` | 1,430 RTTI vftable slots → function addresses (all classes) |
| `cpp/classes.hpp` | Generated C++ skeleton of 43 application classes from vftables (compiles); method names unknown |
| `cpp/AgentProcessControl.cpp` | Hand-lifted C++ for the 6 analyzed functions; names inferred, not compilable as-is |
| `../scripts/ExportDecomp.java`, `VTableMap.java` | Ghidra headless scripts that produced `full/` |
Additional observations from the lift (`cpp/AgentProcessControl.cpp`):
- Watcher thread never closes the process handle → handle leak per launch (observed).
- `LaunchProcess` passes a stack `PROCESS_INFORMATION*` to the watcher thread; safe only because of the following `Sleep(1000)` (observed race, inference about intent).
- `KillProcessByName` leaks the snapshot handle on the success path (observed).
- RMI stubs give the protocol handler tables: `AgentManager2AgentStub` 24 slots (= 7 base + 17 RMIs 0x85fd–0x860d, identical to AgentManager's proxy; see `../AgentManager_Release_x64/evidence.md`), `Server2AgentStub` 19 (`full/vtables.tsv`).
- Server-command switch is `0x14000e1e0` (0 on, 1/3 kill by PID, 4 patch, 7/0xb auto-revive toggles, others forwarded to the server); string commands `serveron`/`auto`/`update` in `0x140005950`.
## Findings
| # | Conclusion | Type | Evidence |
|---|---|---|---|
| 1 | Process supervisor for the other server EXEs: launches them, kills them, watches for exit | **Observed** (code) | `0x1400094e0`, `0x140009780`, `0x140008c90` below |
| 2 | Launch: `CreateProcessW(NULL, cmdline, …, CREATE_NEW_CONSOLE (0x10), …, workdir, …)`; console 700×110, fill attr 0x17; closes thread handle, spawns watcher thread, returns PID (0 on failure, logs `=======Server Fail=======` + Path/File) | Observed | `0x1400094e0.pseudo.c`, `ev_e77cf4b5…` |
| 3 | Command line = `<exe path for server type> + " " + <args>`; args either generated (`FUN_140027540`, a global option flag at `+0x152` selects) or the numeric server code. Logs `FAILED SERVER ON %s` on failure; reports result to AgentManager via RMI proxy | Observed (flow); arg contents **unknown** | `0x140008ea0.pseudo.c`, `ev_e87b1dd7…` |
| 4 | Kill: Toolhelp32 snapshot, case-insensitive exe-name match, `OpenProcess(PROCESS_TERMINATE)`, `TerminateProcess(h,0)` — kills the **first** match only, by image name (not PID) | Observed | `0x140009780.pseudo.c`, `ev_ad71d730…` |
| 5 | Watcher: polls `GetExitCodeProcess` every 10 ms while `STILL_ACTIVE` (0x103); on exit sends two RMI notifications to AgentManager; **relaunches** if per-server revive flag (`+0xd5`) or global flag (`+0x171`) is set | Observed; mapping of flags to `revive_server` XML key is **inference** | `0x140008c90.pseudo.c` |
| 6 | Single instance via named mutex `Global\RustyHearts_Agent`; on ERROR_ALREADY_EXISTS / ACCESS_DENIED prints, sleeps 3 s, returns failure | Observed | `0x1400100b0.pseudo.c` |
| 7 | Main run loop: startup banner, `CoInitialize`, builds network objects, kills a stale process by name before running (type `0x18` entry), loop `Sleep(1)` with 100 ms tick and 1 s FPS counter; optional delayed "Start Auto Server On" after a configured time (`+0x178`) | Observed | `0x1400037a0.pseudo.c`, `ev_2fa3bc1d…` |
| 8 | Networking is **ProudNet** (Nettention) — `Proud::CNetServerImpl`, `CNetClientImpl`, RMI stubs/proxies | Observed (RTTI + assert strings) | strings `ev_47e1666c…` |
| 9 | Agent is both a ProudNet server (for game servers: `SERVER2AGENT_PROTOCOL` stub, `AGENT2SERVER` proxy) and client of AgentManager (`AGENTMANAGER2AGENT` stub, `AGENT2AGENTMANAGER` proxy) | Inference from RTTI class names | strings `ev_47e1666c…` |
| 10 | Config files: `option\agent_serveroption.xml`, `option\agent.xml`, `option\total_option.xml`, `option\patch.xml`, `option\customoption.xml`, `option\service_control.xml` | Observed (strings); which are actually read is **unknown** — `option\agent.xml` referenced from `FUN_1400f06e0` only | `ev_d4bb4bc3…` |
| 11 | Crash reporting via BugTrap (`BT_*` imports) + `MiniDumpWriteDump`; also an HTTP error reporter `GET /PNReport/ReportError.php?Text=` (ProudNet's) | Observed (imports/strings); host contacted **unknown** | imports |
## Named-function map (proposed, not applied in a persistent DB)
| Address | Proposed name |
|---|---|
| `0x1400037a0` | `MagiAgentServer::Run` (main loop) |
| `0x140008ea0` | `ServerOn(serverEntry)` |
| `0x1400094e0` | `LaunchProcess(cmdline) -> pid` |
| `0x140008c90` | `ProcessWatchThread` |
| `0x140009780` | `KillProcessByName(name)` |
| `0x1400100b0` | `AcquireSingleInstanceMutex` |
## Open questions / next steps
- Decode RMI message IDs/handlers for `AGENTMANAGER2AGENT` and `SERVER2AGENT` (commands: All Server On/Down/Kill, Server Info, Shutdown, Notice, Patch).
- Parse config XML loaders (`FUN_1400f06e0`, `MagiServerOption`), and map fields (`start_server`, `revive_server`, ports) to struct offsets.
- Identify `FUN_140027540` (argument builder) to learn exact launch arguments.
- Locate ProudNet SDK version to label library code (likely large share of the 6,814 functions).